diff --git a/README.md b/README.md index 170f2e8cb..03f140dbc 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ ![Fedora](https://img.shields.io/badge/LINUX-Fedora-blue?style=for-the-badge&logo=Fedora) [![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki) +[![](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge?style=for-the-badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web) * SSH终端工具 * 面板收藏功能 @@ -59,6 +60,19 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443: * mysql主从配置。 * swap插件[虚拟内存]。 +### jsdelivr [ 发布新版本生效 ] + +- 初始安装 + +``` +curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash +``` + +- 更新 + +``` +curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash +``` ### GW使用 @@ -90,7 +104,7 @@ curl -fsSL https://gitee.com/midoks/mdserver-web/raw/master/scripts/update_cn.s -### 通用安装 +### 通用软件安装 ``` curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash diff --git a/class/core/config_api.py b/class/core/config_api.py index 5c5b49dd5..429b3346d 100755 --- a/class/core/config_api.py +++ b/class/core/config_api.py @@ -15,9 +15,8 @@ from flask import request class config_api: - # 进行中. - # 兼容主流Linux系统 - __version = '0.8.6.13' + # mariadb 优化 + __version = '0.8.6.16' def __init__(self): pass diff --git a/class/core/mw.py b/class/core/mw.py index 6ca763223..70141afea 100755 --- a/class/core/mw.py +++ b/class/core/mw.py @@ -98,7 +98,12 @@ def systemdCfgDir(): return cfg_dir # debian,centos - return "/usr/lib/systemd/system" + cfg_dir = '/usr/lib/systemd/system' + if os.path.exists(cfg_dir): + return cfg_dir + + # local test + return "/tmp" def getOs(): @@ -261,6 +266,17 @@ def getRandomString(length): return str +def getUniqueId(): + """ + 根据时间生成唯一ID + :return: + """ + current_time = datetime.datetime.now() + str_time = current_time.strftime('%Y%m%d%H%M%S%f')[:-3] + unique_id = "{0}".format(str_time) + return unique_id + + def getJson(data): import json return json.dumps(data) diff --git a/class/core/site_api.py b/class/core/site_api.py index 0593284d6..ad597ada0 100755 --- a/class/core/site_api.py +++ b/class/core/site_api.py @@ -727,14 +727,14 @@ class site_api: file = self.getHostConf(siteName) conf = mw.readFile(file) if conf: - # if conf.find('ssl_certificate') == -1: - # return mw.returnJson(False, '当前未开启SSL') + if conf.find('ssl_certificate') == -1: + return mw.returnJson(False, '当前未开启SSL') to = """#error_page 404/404.html; - # HTTP_TO_HTTPS_START + #HTTP_TO_HTTPS_START if ($server_port !~ 443){ rewrite ^(/.*)$ https://$host$1 permanent; } - # HTTP_TO_HTTPS_END""" + #HTTP_TO_HTTPS_END""" conf = conf.replace('#error_page 404/404.html;', to) mw.writeFile(file, conf) @@ -2141,20 +2141,21 @@ location ^~ {from} { content = content.replace('{$LOGPATH}', logsPath) mw.writeFile(vhost_file, content) - rewrite_content = ''' -location /{ - if ($PHP_ENV != "1"){ - break; - } - - if (!-e $request_filename) { - rewrite ^(.*)$ /index.php/$1 last; - break; - } -} -''' +# 和反代配置冲突 && 默认伪静态为空 +# rewrite_content = ''' +# location /{ +# if ($PHP_ENV != "1"){ +# break; +# } + +# if (!-e $request_filename) { +# rewrite ^(.*)$ /index.php/$1 last; +# break; +# } +# } +# ''' rewrite_file = self.rewritePath + '/' + self.siteName + '.conf' - mw.writeFile(rewrite_file, rewrite_content) + mw.writeFile(rewrite_file, '') def add(self, webname, port, ps, path, version): siteMenu = json.loads(webname) diff --git a/class/plugin/orm.py b/class/plugin/orm.py index 64ffd1790..aac2b9e36 100755 --- a/class/plugin/orm.py +++ b/class/plugin/orm.py @@ -24,13 +24,26 @@ class ORM: '''连接数据库''' try: - try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, cursorclass=pymysql.cursors.DictCursor) - except Exception as e: - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, cursorclass=pymysql.cursors.DictCursor) + if os.path.exists(self.__DB_SOCKET): + try: + self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, + port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, + unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) + except Exception as e: + self.__DB_HOST = '127.0.0.1' + self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, + port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, + unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) + else: + try: + self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, + port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, + cursorclass=pymysql.cursors.DictCursor) + except Exception as e: + self.__DB_HOST = '127.0.0.1' + self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, + port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, + cursorclass=pymysql.cursors.DictCursor) self.__DB_CUR = self.__DB_CONN.cursor() return True diff --git a/plugins/mariadb/conf/classic.cnf b/plugins/mariadb/conf/classic.cnf new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/mariadb/conf/gtid.cnf b/plugins/mariadb/conf/gtid.cnf new file mode 100644 index 000000000..5076776a6 --- /dev/null +++ b/plugins/mariadb/conf/gtid.cnf @@ -0,0 +1,4 @@ +[mysqld] +# SHOW GLOBAL VARIABLES LIKE '%gtid%' +gtid_mode=ON +enforce_gtid_consistency=ON \ No newline at end of file diff --git a/plugins/mariadb/conf/mariadb.sql b/plugins/mariadb/conf/mariadb.sql index 38b822738..f6df3e957 100755 --- a/plugins/mariadb/conf/mariadb.sql +++ b/plugins/mariadb/conf/mariadb.sql @@ -12,9 +12,11 @@ CREATE TABLE IF NOT EXISTS `databases` ( `username` TEXT, `password` TEXT, `accept` TEXT, + `rw` TEXT DEFAULT 'rw', `ps` TEXT, `addtime` TEXT ); +-- ALTER TABLE `databases` ADD COLUMN `rw` TEXT DEFAULT 'rw'; CREATE TABLE IF NOT EXISTS `master_replication_user` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -25,4 +27,17 @@ CREATE TABLE IF NOT EXISTS `master_replication_user` ( `addtime` TEXT ); +-- 从库配置主库的[ssh private key] +-- drop table `slave_id_rsa`; +CREATE TABLE IF NOT EXISTS `slave_id_rsa` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `ip` TEXT, + `port` TEXT, + `user` TEXT, + `db_user` TEXT, + `id_rsa` TEXT, + `ps` TEXT, + `addtime` TEXT +); + diff --git a/plugins/mariadb/conf/my.cnf b/plugins/mariadb/conf/my.cnf index 4fd989170..8851e5b21 100644 --- a/plugins/mariadb/conf/my.cnf +++ b/plugins/mariadb/conf/my.cnf @@ -5,6 +5,9 @@ port = 33106 socket = {$SERVER_APP_PATH}/mysql.sock [mysqld] + +!include {$SERVER_APP_PATH}/etc/mode/classic.cnf + pid-file = {$SERVER_APP_PATH}/data/mysql.pid user = mysql port = 33106 @@ -12,7 +15,10 @@ socket = {$SERVER_APP_PATH}/mysql.sock basedir = {$SERVER_APP_PATH} datadir = {$SERVER_APP_PATH}/data log-error = {$SERVER_APP_PATH}/data/error.log -default_storage_engine = MyISAM +server-id = {$SERVER_ID} + +default_storage_engine = InnoDB + key_buffer_size = 8M max_allowed_packet = 100M table_open_cache = 32 @@ -30,8 +36,8 @@ max_connections = 500 max_connect_errors = 100 open_files_limit = 65535 +skip-name-resolve = on #skip-networking -#skip-name-resolve #skip-external-locking #loose-skip-innodb #skip-grant-tables @@ -39,7 +45,6 @@ open_files_limit = 65535 log-bin=mysql-bin binlog_format=mixed -server-id = 1 expire_logs_days = 10 slow_query_log=1 slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log @@ -64,7 +69,7 @@ replicate-ignore-db = performance_schema replicate-ignore-db = mysql replicate-ignore-db = test -default_storage_engine = InnoDB + innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_file_path = ibdata1:10M:autoextend innodb_log_group_home_dir = {$SERVER_APP_PATH}/data @@ -75,7 +80,7 @@ innodb_flush_log_at_trx_commit = 1 innodb_lock_wait_timeout = 120 innodb_max_dirty_pages_pct = 90 innodb_read_io_threads = 1 -innodb_write_io_threads = 1 +innodb_write_io_threads = 2 innodb_file_per_table=1 diff --git a/plugins/mariadb/index.html b/plugins/mariadb/index.html index 80a73bb58..24065d6e0 100755 --- a/plugins/mariadb/index.html +++ b/plugins/mariadb/index.html @@ -13,6 +13,7 @@

日志

慢日志

管理列表

+

主从配置

diff --git a/plugins/mariadb/index.py b/plugins/mariadb/index.py index e41d47cc4..9ba058bd2 100755 --- a/plugins/mariadb/index.py +++ b/plugins/mariadb/index.py @@ -120,6 +120,13 @@ def contentReplace(content): content = content.replace('{$ROOT_PATH}', mw.getRootDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP_PATH}', service_path + '/mariadb') + server_id = int(time.time()) + content = content.replace('{$SERVER_ID}', str(server_id)) + + if mw.isAppleSystem(): + user = mw.execShell( + "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() + content = content.replace('user = mysql', 'user = ' + user) return content @@ -158,6 +165,25 @@ def pMysqlDb(): return db +def makeInitRsaKey(version=''): + datadir = getServerDir() + "/data" + + mysql_pem = datadir + "/mysql.pem" + if not os.path.exists(mysql_pem): + rdata = mw.execShell( + 'cd ' + datadir + ' && openssl genrsa -out mysql.pem 1024') + # print(data) + rdata = mw.execShell( + 'cd ' + datadir + ' && openssl rsa -in mysql.pem -pubout -out mysql.pub') + # print(rdata) + + if not mw.isAppleSystem(): + mw.execShell('cd ' + datadir + ' && chmod 400 mysql.pem') + mw.execShell('cd ' + datadir + ' && chmod 444 mysql.pub') + mw.execShell('cd ' + datadir + ' && chown mysql:mysql mysql.pem') + mw.execShell('cd ' + datadir + ' && chown mysql:mysql mysql.pub') + + def initDreplace(version=''): initd_tpl = getInitdTpl(version) @@ -306,28 +332,36 @@ def initMysqlData(): cmd = 'cd ' + serverdir + ' && ./scripts/mariadb-install-db ' + \ ' --defaults-file=' + myconf data = mw.execShell(cmd) - # print(data) + # print(data[0]) + # print(data[1]) return False return True -def initMysqlPwd(): +def initMariaDbPwd(): time.sleep(5) serverdir = getServerDir() pwd = mw.getRandomString(16) - cmd_pass = serverdir + '/bin/mysql -S ' + serverdir + '/mysql.sock -uroot -e' + db_option = "-S " + serverdir + "/mysql.sock" + cmd_pass = serverdir + '/bin/mysql ' + db_option + ' -uroot -e' cmd_pass = cmd_pass + \ - "\"grant all privileges on *.* to 'root'@'localhost' identified by '" + pwd + "';" + "\"flush privileges;use mysql;grant all privileges on *.* to 'root'@'localhost' identified by '" + pwd + "';" cmd_pass = cmd_pass + "flush privileges;\"" + # print(cmd_pass) data = mw.execShell(cmd_pass) # print(data) if data[1].find("ERROR") != -1: print("init mariadb password fail:" + data[1]) exit(1) + # 删除测试数据库 + drop_test_db = serverdir + '/bin/mysql ' + db_option + ' -uroot -p' + \ + pwd + ' -e "drop database test";' + mw.execShell(drop_test_db) + pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (pwd,)) return True @@ -335,20 +369,46 @@ def initMysqlPwd(): def myOp(version, method): # import commands init_file = initDreplace() + cmd = init_file + ' ' + method try: isInited = initMysqlData() if not isInited: - mw.execShell('systemctl start mariadb') - initMysqlPwd() - mw.execShell('systemctl stop mariadb') + if mw.isAppleSystem(): + setSkipGrantTables(True) + cmd_init_start = init_file + ' start' + subprocess.Popen(cmd_init_start, stdout=subprocess.PIPE, shell=True, + bufsize=4096, stderr=subprocess.PIPE) + + time.sleep(6) + else: + mw.execShell('systemctl start mariadb') + + initMariaDbPwd() + + if mw.isAppleSystem(): + setSkipGrantTables(False) + cmd_init_stop = init_file + ' stop' + subprocess.Popen(cmd_init_stop, stdout=subprocess.PIPE, shell=True, + bufsize=4096, stderr=subprocess.PIPE) + time.sleep(3) + else: + mw.execShell('systemctl stop mariadb') + + if mw.isAppleSystem(): + print + sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, + bufsize=4096, stderr=subprocess.PIPE) + sub.wait(5) + else: + mw.execShell('systemctl ' + method + ' mariadb') - mw.execShell('systemctl ' + method + ' mariadb') return 'ok' except Exception as e: return str(e) def appCMD(version, action): + makeInitRsaKey(version) return myOp(version, action) @@ -493,8 +553,8 @@ def runInfo(): result['Run'] = int(time.time()) - int(result['Uptime']) tmp = db.query('show master status') try: - result['File'] = tmp[0][0] - result['Position'] = tmp[0][1] + result['File'] = tmp[0]["File"] + result['Position'] = tmp[0]["Position"] except: result['File'] = 'OFF' result['Position'] = 'OFF' @@ -570,16 +630,6 @@ def isSqlError(mysqlMsg): return None -def mapToList(map_obj): - # map to list - try: - if type(map_obj) != list and type(map_obj) != str: - map_obj = list(map_obj) - return map_obj - except: - return [] - - def __createUser(dbname, username, password, address): pdb = pMysqlDb() @@ -618,9 +668,8 @@ def setDbBackup(): if not data[0]: return data[1] - scDir = mw.getRunDir() + '/scripts/backup.py' - - cmd = 'python ' + scDir + ' database ' + args['name'] + ' 3' + scDir = getPluginDir() + '/scripts/backup.py' + cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3' os.system(cmd) return mw.returnJson(True, 'ok') @@ -711,7 +760,7 @@ def getDbList(): condition = '' if not search == '': condition = "name like '%" + search + "%'" - field = 'id,pid,name,username,password,accept,ps,addtime' + field = 'id,pid,name,username,password,accept,rw,ps,addtime' clist = conn.where(condition, ()).field( field).limit(limit).order('id desc').select() @@ -836,7 +885,7 @@ def syncToDatabases(): return mw.returnJson(True, msg) -def setRootPwd(): +def setRootPwd(version=''): args = getArgs() data = checkArgs(args, ['password']) if not data[0]: @@ -850,17 +899,8 @@ def setRootPwd(): if isError != None: return isError - m_version = mw.readFile(getServerDir() + '/version.pl') - if m_version.find('5.7') == 0 or m_version.find('8.0') == 0: - pdb.execute( - "UPDATE mysql.user SET authentication_string='' WHERE user='root'") - pdb.execute( - "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s'" % password) - pdb.execute( - "ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '%s'" % password) - else: - result = pdb.execute( - "update mysql.user set Password=password('" + password + "') where User='root'") + result = pdb.execute( + "update mysql.user set Password=password('" + password + "') where User='root'") pdb.execute("flush privileges") pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (password,)) return mw.returnJson(True, '数据库root密码修改成功!') @@ -868,7 +908,7 @@ def setRootPwd(): return mw.returnJson(False, '修改错误:' + str(ex)) -def setUserPwd(): +def setUserPwd(version=''): args = getArgs() data = checkArgs(args, ['password', 'name']) if not data[0]: @@ -882,27 +922,14 @@ def setUserPwd(): psdb = pSqliteDb('databases') name = psdb.where('id=?', (uid,)).getField('name') - m_version = mw.readFile(getServerDir() + '/version.pl') - if m_version.find('5.7') == 0 or m_version.find('8.0') == 0: - accept = pdb.query( - "select Host from mysql.user where User='" + name + "' AND Host!='localhost'") - pdb.execute( - "update mysql.user set authentication_string='' where User='" + username + "'") - result = pdb.execute( - "ALTER USER `%s`@`localhost` IDENTIFIED BY '%s'" % (username, newpassword)) - for my_host in accept: - pdb.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % ( - username, my_host["Host"], newpassword)) - else: - result = pdb.execute("update mysql.user set Password=password('" + - newpassword + "') where User='" + username + "'") + result = pdb.execute("update mysql.user set Password=password('" + + newpassword + "') where User='" + username + "'") pdb.execute("flush privileges") - psdb.where("id=?", (id,)).setField('password', newpassword) + psdb.where("id=?", (uid,)).setField('password', newpassword) return mw.returnJson(True, mw.getInfo('修改数据库[{1}]密码成功!', (name,))) except Exception as ex: - print(str(ex)) - return mw.returnJson(False, mw.getInfo('修改数据库[{1}]密码失败!', (name,))) + return mw.returnJson(False, mw.getInfo('修改数据库[{1}]密码失败[{2}]!', (name, str(ex),))) def setDbPs(): @@ -955,8 +982,8 @@ def addDb(): wheres = { 'utf8': 'utf8_general_ci', - 'utf8mb4': 'utf8mb4_general_ci', - 'gbk': 'gbk_chinese_ci', + 'utf8mb4': 'utf8mb4_general_ci', + 'gbk': 'gbk_chinese_ci', 'big5': 'big5_chinese_ci' } codeStr = wheres[codeing] @@ -1029,10 +1056,6 @@ def getDbAccess(): users = pdb.query("select Host from mysql.user where User='" + username + "' AND Host!='localhost'") - # isError = isSqlError(users) - # if isError != None: - # return isError - if len(users) < 1: return mw.returnJson(True, "127.0.0.1") accs = [] @@ -1042,19 +1065,6 @@ def getDbAccess(): return mw.returnJson(True, userStr) -def toSize(size): - d = ('b', 'KB', 'MB', 'GB', 'TB') - s = d[0] - for b in d: - if size < 1024: - return str(size) + ' ' + b - size = size / 1024 - s = b - _size = round(size, 2) - # print(size, _size) - return str(size) + ' ' + b - - def setDbAccess(): args = getArgs() data = checkArgs(args, ['username', 'access']) @@ -1081,10 +1091,51 @@ def setDbAccess(): __createUser(dbname, name, password, access) - psdb.where('username=?', (name,)).save('accept', (access,)) + psdb.where('username=?', (name,)).save('accept,rw', (access, 'rw',)) return mw.returnJson(True, '设置成功!') +def setDbRw(version=''): + args = getArgs() + data = checkArgs(args, ['username', 'id', 'rw']) + if not data[0]: + return data[1] + + username = args['username'] + uid = args['id'] + rw = args['rw'] + + pdb = pMysqlDb() + psdb = pSqliteDb('databases') + dbname = psdb.where("id=?", (uid,)).getField('name') + users = pdb.query( + "select Host from mysql.user where User='" + username + "'") + + # show grants for demo@"127.0.0.1"; + for x in users: + # REVOKE ALL PRIVILEGES ON `imail`.* FROM 'imail'@'127.0.0.1'; + + sql = "REVOKE ALL PRIVILEGES ON `" + dbname + \ + "`.* FROM '" + username + "'@'" + x["Host"] + "';" + r = pdb.query(sql) + # print(sql, r) + + if rw == 'rw': + sql = "GRANT SELECT, INSERT, UPDATE, DELETE ON " + dbname + ".* TO " + \ + username + "@'" + x["Host"] + "'" + elif rw == 'r': + sql = "GRANT SELECT ON " + dbname + ".* TO " + \ + username + "@'" + x["Host"] + "'" + else: + sql = "GRANT all privileges ON " + dbname + ".* TO " + \ + username + "@'" + x["Host"] + "'" + pdb.execute(sql) + pdb.execute("flush privileges") + r = psdb.where("id=?", (uid,)).setField('rw', rw) + # print(r) + return mw.returnJson(True, '切换成功!') + + def getDbInfo(): args = getArgs() data = checkArgs(args, ['name']) @@ -1097,9 +1148,12 @@ def getDbInfo(): tables = pdb.query('show tables from `%s`' % db_name) ret = {} - data_sum = pdb.query( - "select sum(DATA_LENGTH)+sum(INDEX_LENGTH) as sum_size from information_schema.tables where table_schema='%s'" % db_name) - data = data_sum[0]['sum_size'] + sql = "select sum(DATA_LENGTH)+sum(INDEX_LENGTH) as sum_size from information_schema.tables where table_schema='%s'" % db_name + data_sum = pdb.query(sql) + + data = 0 + if data_sum[0]['sum_size'] != None: + data = data_sum[0]['sum_size'] ret['data_size'] = mw.toSize(data) ret['database'] = db_name @@ -1107,14 +1161,22 @@ def getDbInfo(): ret3 = [] table_key = "Tables_in_" + db_name for i in tables: - table = pdb.query( - "show table status from `%s` where name = '%s'" % (db_name, i[table_key])) + tb_sql = "show table status from `%s` where name = '%s'" % (db_name, i[ + table_key]) + table = pdb.query(tb_sql) tmp = {} tmp['type'] = table[0]["Engine"] tmp['rows_count'] = table[0]["Rows"] tmp['collation'] = table[0]["Collation"] - data_size = table[0]["Avg_row_length"] + table[0]["Data_length"] + + data_size = 0 + if table[0]['Avg_row_length'] != None: + data_size = table[0]['Avg_row_length'] + + if table[0]['Data_length'] != None: + data_size = table[0]['Data_length'] + tmp['data_byte'] = data_size tmp['data_size'] = mw.toSize(data_size) tmp['table_name'] = table[0]["Name"] @@ -1218,6 +1280,74 @@ def getTotalStatistics(): return mw.returnJson(False, 'fail', data) +def recognizeDbMode(): + conf = getConf() + con = mw.readFile(conf) + rep = r"!include %s/(.*)?\.cnf" % (getServerDir() + "/etc/mode",) + mode = 'none' + try: + data = re.findall(rep, con, re.M) + mode = data[0] + except Exception as e: + pass + return mode + + +def getDbrunMode(version=''): + mode = recognizeDbMode() + return mw.returnJson(True, "ok", {'mode': mode}) + + +def setDbrunMode(version=''): + if version == '5.5': + return mw.returnJson(False, "不支持切换") + + args = getArgs() + data = checkArgs(args, ['mode', 'reload']) + if not data[0]: + return data[1] + + mode = args['mode'] + dbreload = args['reload'] + + if not mode in ['classic', 'gtid']: + return mw.returnJson(False, "mode的值无效:" + mode) + + origin_mode = recognizeDbMode() + path = getConf() + con = mw.readFile(path) + rep = r"!include %s/%s\.cnf" % (getServerDir() + "/etc/mode", origin_mode) + rep_after = "!include %s/%s.cnf" % (getServerDir() + "/etc/mode", mode) + con = re.sub(rep, rep_after, con) + mw.writeFile(path, con) + + if version == '5.6': + dbreload = 'yes' + else: + db = pMysqlDb() + # The value of @@GLOBAL.GTID_MODE can only be changed one step at a + # time: OFF <-> OFF_PERMISSIVE <-> ON_PERMISSIVE <-> ON. Also note that + # this value must be stepped up or down simultaneously on all servers. + # See the Manual for instructions. + if mode == 'classic': + db.query('set global enforce_gtid_consistency=off') + db.query('set global gtid_mode=on') + db.query('set global gtid_mode=on_permissive') + db.query('set global gtid_mode=off_permissive') + db.query('set global gtid_mode=off') + elif mode == 'gtid': + db.query('set global enforce_gtid_consistency=on') + db.query('set global gtid_mode=off') + db.query('set global gtid_mode=off_permissive') + db.query('set global gtid_mode=on_permissive') + db.query('set global gtid_mode=on') + + if dbreload == "yes": + restart(version) + + return mw.returnJson(True, "切换成功!") + + def findBinlogDoDb(): conf = getConf() con = mw.readFile(conf) @@ -1234,6 +1364,33 @@ def findBinlogSlaveDoDb(): return dodb +def setDbMasterAccess(): + args = getArgs() + data = checkArgs(args, ['username', 'access']) + if not data[0]: + return data[1] + username = args['username'] + access = args['access'] + pdb = pMysqlDb() + psdb = pSqliteDb('master_replication_user') + password = psdb.where("username=?", (username,)).getField('password') + users = pdb.query("select Host from mysql.user where User='" + + username + "' AND Host!='localhost'") + for us in users: + pdb.execute("drop user '" + username + "'@'" + us["Host"] + "'") + + dbname = '*' + for a in access.split(','): + pdb.execute( + "CREATE USER `%s`@`%s` IDENTIFIED BY '%s'" % (username, a, password)) + pdb.execute( + "grant all privileges on %s.* to `%s`@`%s`" % (dbname, username, a)) + + pdb.execute("flush privileges") + psdb.where('username=?', (username,)).save('accept', (access,)) + return mw.returnJson(True, '设置成功!') + + def getMasterDbList(version=''): args = getArgs() page = 1 @@ -1353,13 +1510,15 @@ def getMasterStatus(version=''): return mw.returnJson(False, 'MySQL未启动,或正在启动中...!', []) conf = getConf() - con = mw.readFile(conf) + content = mw.readFile(conf) master_status = False - if con.find('#log-bin') == -1 and con.find('log-bin') > 1: + if content.find('#log-bin') == -1 and content.find('log-bin') > 1: dodb = findBinlogDoDb() if len(dodb) > 0: master_status = True + data = {} + data['mode'] = recognizeDbMode() data['status'] = master_status db = pMysqlDb() @@ -1437,10 +1596,6 @@ def getMasterRepSlaveList(version=''): def addMasterRepSlaveUser(version=''): - version_pl = getServerDir() + "/version.pl" - if os.path.exists(version_pl): - version = mw.readFile(version_pl).strip() - args = getArgs() data = checkArgs(args, ['username', 'password']) if not data[0]: @@ -1475,25 +1630,13 @@ def addMasterRepSlaveUser(version=''): if psdb.where("username=?", (username)).count() > 0: return mw.returnJson(False, '用户已存在!') - if version == "8.0": - sql = "CREATE USER '" + username + \ - "' IDENTIFIED WITH mysql_native_password BY '" + password + "';" - pdb.execute(sql) - sql = "grant replication slave on *.* to '" + username + "'@'%';" - result = pdb.execute(sql) - isError = isSqlError(result) - if isError != None: - return isError - - sql = "FLUSH PRIVILEGES;" - pdb.execute(sql) - else: - sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \ - "'@'%' identified by '" + password + "';FLUSH PRIVILEGES;" - result = pdb.execute(sql) - isError = isSqlError(result) - if isError != None: - return isError + sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \ + "'@'%' identified by '" + password + "';" + result = pdb.execute(sql) + result = pdb.execute('FLUSH PRIVILEGES;') + isError = isSqlError(result) + if isError != None: + return isError addTime = time.strftime('%Y-%m-%d %X', time.localtime()) psdb.add('username,password,accept,ps,addtime', @@ -1503,10 +1646,6 @@ def addMasterRepSlaveUser(version=''): def getMasterRepSlaveUserCmd(version): - version_pl = getServerDir() + "/version.pl" - if os.path.exists(version_pl): - version = mw.readFile(version_pl).strip() - args = getArgs() data = checkArgs(args, ['username', 'db']) if not data[0]: @@ -1514,40 +1653,44 @@ def getMasterRepSlaveUserCmd(version): psdb = pSqliteDb('master_replication_user') f = 'username,password' - if args['username'] == '': - + username = args['username'] + if username == '': count = psdb.count() - if count == 0: return mw.returnJson(False, '请添加同步账户!') clist = psdb.field(f).limit('1').order('id desc').select() else: - clist = psdb.field(f).where("username=?", (args['username'],)).limit( + clist = psdb.field(f).where("username=?", (username,)).limit( '1').order('id desc').select() ip = mw.getLocalIp() port = getMyPort() - db = pMysqlDb() + mstatus = db.query('show master status') if len(mstatus) == 0: return mw.returnJson(False, '未开启!') - sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ - clist[0]['username'] + "', MASTER_PASSWORD='" + \ - clist[0]['password'] + \ - "', MASTER_LOG_FILE='" + mstatus[0]["File"] + \ - "',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + "" + mode = recognizeDbMode() - if version == "8.0": - sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ - clist[0]['username'] + "', SOURCE_PASSWORD='" + \ + if mode == "gtid": + sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ + clist[0]['username'] + "', MASTER_PASSWORD='" + \ + clist[0]['password'] + "', MASTER_AUTO_POSITION=1" + else: + sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ + clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['password'] + \ - "', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \ - "',SOURCE_LOG_POS=" + str(mstatus[0]["Position"]) + "" + "', MASTER_LOG_FILE='" + mstatus[0]["File"] + \ + "',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) - return mw.returnJson(True, 'OK!', sql) + data = {} + data['cmd'] = sql + data["info"] = clist[0] + data['mode'] = mode + + return mw.returnJson(True, 'ok!', data) def delMasterRepSlaveUser(version=''): @@ -1556,9 +1699,18 @@ def delMasterRepSlaveUser(version=''): if not data[0]: return data[1] + name = args['username'] + pdb = pMysqlDb() psdb = pSqliteDb('master_replication_user') - pdb.execute("drop user '" + args['username'] + "'@'%'") + pdb.execute("drop user '" + name + "'@'%'") + pdb.execute("drop user '" + name + "'@'localhost'") + + users = pdb.query("select Host from mysql.user where User='" + + name + "' AND Host!='localhost'") + for us in users: + pdb.execute("drop user '" + name + "'@'" + us["Host"] + "'") + psdb.where("username=?", (args['username'],)).delete() return mw.returnJson(True, '删除成功!') @@ -1595,7 +1747,7 @@ def getSlaveSSHList(version=''): conn = pSqliteDb('slave_id_rsa') limit = str((page - 1) * page_size) + ',' + str(page_size) - field = 'id,ip,port,id_rsa,ps,addtime' + field = 'id,ip,port,db_user,id_rsa,ps,addtime' clist = conn.field(field).limit(limit).order('id desc').select() count = conn.count() @@ -1620,7 +1772,7 @@ def getSlaveSSHByIp(version=''): ip = args['ip'] conn = pSqliteDb('slave_id_rsa') - data = conn.field('ip,port,id_rsa').where("ip=?", (ip,)).select() + data = conn.field('ip,port,db_user,id_rsa').where("ip=?", (ip,)).select() return mw.returnJson(True, 'ok', data) @@ -1636,22 +1788,24 @@ def addSlaveSSH(version=''): if ip == "": return mw.returnJson(True, 'ok') - data = checkArgs(args, ['port', 'id_rsa']) + data = checkArgs(args, ['port', 'id_rsa', 'db_user']) if not data[0]: return data[1] id_rsa = args['id_rsa'] port = args['port'] + db_user = args['db_user'] user = 'root' addTime = time.strftime('%Y-%m-%d %X', time.localtime()) conn = pSqliteDb('slave_id_rsa') data = conn.field('ip,id_rsa').where("ip=?", (ip,)).select() if len(data) > 0: - res = conn.where("ip=?", (ip,)).save('port,id_rsa', (port, id_rsa,)) + res = conn.where("ip=?", (ip,)).save( + 'port,id_rsa,db_user', (port, id_rsa, db_user)) else: - conn.add('ip,port,user,id_rsa,ps,addtime', - (ip, port, user, id_rsa, '', addTime)) + conn.add('ip,port,user,id_rsa,db_user,ps,addtime', + (ip, port, user, id_rsa, db_user, '', addTime)) return mw.returnJson(True, '设置成功!') @@ -1686,6 +1840,8 @@ def getSlaveList(version=''): db = pMysqlDb() dlist = db.query('show slave status') + + # print(dlist) ret = [] for x in range(0, len(dlist)): tmp = {} @@ -1705,66 +1861,139 @@ def getSlaveList(version=''): def getSlaveSyncCmd(version=''): root = mw.getRunDir() - cmd = 'cd ' + root + ' && python ' + root + \ + cmd = 'cd ' + root + ' && python3 ' + root + \ '/plugins/mysql/index.py do_full_sync {"db":"all"}' return mw.returnJson(True, 'ok', cmd) +def initSlaveStatus(version=''): + db = pMysqlDb() + dlist = db.query('show slave status') + if len(dlist) > 0: + return mw.returnJson(False, '已经初始化好了zz...') + + conn = pSqliteDb('slave_id_rsa') + data = conn.field('ip,port,id_rsa,db_user').find() + + if len(data) < 1: + 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 + paramiko.util.log_to_file('paramiko.log') + ssh = paramiko.SSHClient() + + try: + + 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'] + cmd = 'cd /www/server/mdserver-web && python3 plugins/mariadb/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}' + stdin, stdout, stderr = ssh.exec_command(cmd) + result = stdout.read() + result = result.decode('utf-8') + 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() + if local_mode != cmd_data['data']['mode']: + return mw.returnJson(False, '主【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode)) + + u = cmd_data['data']['info'] + ps = u['username'] + "|" + u['password'] + conn.where('ip=?', (ip,)).setField('ps', ps) + db.query('stop slave') + + # 保证同步IP一致 + cmd = cmd_data['data']['cmd'] + if cmd.find('SOURCE_HOST') > -1: + cmd = re.sub(r"SOURCE_HOST='(.*?)'", + "SOURCE_HOST='" + ip + "'", cmd, 1) + + if cmd.find('MASTER_HOST') > -1: + cmd = re.sub(r"MASTER_HOST='(.*?)'", + "MASTER_HOST='" + ip + "'", cmd, 1) + + # print(cmd) + db.query(cmd) + db.query("start slave") + + except Exception as e: + return mw.returnJson(False, 'SSH认证配置连接失败!' + str(e)) + + os.system("rm -rf " + SSH_PRIVATE_KEY) + return mw.returnJson(True, '初始化成功!') + + def setSlaveStatus(version=''): + db = pMysqlDb() dlist = db.query('show slave status') if len(dlist) == 0: - return mw.returnJson(False, '需要手动添加主服务同步命令!') + 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: - db.query('start slave') + 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, '设置成功!') def deleteSlave(version=''): db = pMysqlDb() - dlist = db.query('stop slave;reset slave all') + dlist = db.query('stop slave') + dlist = db.query('reset slave all') return mw.returnJson(True, '删除成功!') -def dumpMysqlData(version): - +def dumpMysqlData(version=''): args = getArgs() data = checkArgs(args, ['db']) if not data[0]: return data[1] pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') - if args['db'] == 'all' or args['db'] == 'ALL': + mysql_dir = getServerDir() + myconf = mysql_dir + "/etc/my.cnf" + + option = '' + mode = recognizeDbMode() + if mode == 'gtid': + option = ' --set-gtid-purged=off ' + + if args['db'].lower() == 'all': dlist = findBinlogDoDb() - cmd = getServerDir() + "/bin/mysqldump -uroot -p" + \ - pwd + " --databases " + ' '.join(dlist) + \ - " > /tmp/dump.sql" + cmd = mysql_dir + "/bin/mysqldump --defaults-file=" + myconf + " " + option + " -uroot -p" + \ + pwd + " --databases " + \ + ' '.join(dlist) + " | gzip > /tmp/dump.sql.gz" else: - cmd = getServerDir() + "/bin/mysqldump -uroot -p" + pwd + \ - " --databases " + args['db'] + " > /tmp/dump.sql" + cmd = mysql_dir + "/bin/mysqldump --defaults-file=" + myconf + " " + option + " -uroot -p" + \ + pwd + " --databases " + args['db'] + " | gzip > /tmp/dump.sql.gz" ret = mw.execShell(cmd) - if ret[0] == '': return 'ok' return 'fail' -from threading import Thread -from time import sleep - - -def mw_async(f): - def wrapper(*args, **kwargs): - thr = Thread(target=f, args=args, kwargs=kwargs) - thr.start() - return wrapper - - ############### --- 重要 同步---- ########### def writeDbSyncStatus(data): @@ -1775,37 +2004,26 @@ def writeDbSyncStatus(data): mw.writeFile(path, json.dumps(data)) -def doFullSync(): +def doFullSync(version=''): args = getArgs() data = checkArgs(args, ['db']) if not data[0]: return data[1] - arg_db_select = args['db'] - - status_data = {} - status_data['progress'] = 5 - db = pMysqlDb() - dlist = db.query('show slave status') - if len(dlist) == 0: - status_data['code'] = -1 - status_data['msg'] = '没有启动...' - - ip = dlist[0]["Master_Host"] - print("master ip:", ip) - id_rsa_conn = pSqliteDb('slave_id_rsa') - data = id_rsa_conn.field('ip,port,id_rsa').where("ip=?", (ip,)).select() + data = id_rsa_conn.field('ip,port,db_user,id_rsa').find() SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt" - id_rsa_key = data[0]['id_rsa'] - id_rsa_key = id_rsa_key.replace('\\n', '\n') - master_port = int(data[0]['port']) + id_rsa = data['id_rsa'].replace('\\n', '\n') + mw.writeFile(SSH_PRIVATE_KEY, id_rsa) - mw.writeFile(SSH_PRIVATE_KEY, id_rsa_key) + ip = data["ip"] + master_port = data['port'] + db_user = data['db_user'] + print("master ip:", ip) writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0}) @@ -1819,15 +2037,15 @@ def doFullSync(): return 'fail' try: + # ssh.load_system_host_keys() mw.execShell("chmod 600 " + SSH_PRIVATE_KEY) key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY) - # ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) print(ip, master_port) # pkey=key # key_filename=SSH_PRIVATE_KEY - ssh.connect(hostname=ip, port=master_port, + ssh.connect(hostname=ip, port=int(master_port), username='root', pkey=key) except Exception as e: print(str(e)) @@ -1836,53 +2054,69 @@ def doFullSync(): return 'fail' writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5}) - - cmd = "cd /www/server/mdserver-web && python /www/server/mdserver-web/plugins/mysql/index.py dump_mysql_data {\"db\":'" + args[ - 'db'] + "'}" + dbname = args['db'] + cmd = "cd /www/server/mdserver-web && python3 plugins/mariadb/index.py dump_mysql_data {\"db\":'" + dbname + "'}" + print(cmd) stdin, stdout, stderr = ssh.exec_command(cmd) result = stdout.read() - result_err = stderr.read() - result = result.decode('utf-8') - # print(result) if result.strip() == 'ok': writeDbSyncStatus({'code': 1, 'msg': '主服务器备份完成...', 'progress': 30}) else: - writeDbSyncStatus({'code': 1, 'msg': '主服务器备份失败...', 'progress': 30}) + writeDbSyncStatus( + {'code': 1, 'msg': '主服务器备份失败...:' + str(result), 'progress': 100}) return 'fail' - r = mw.execShell('scp root@' + ip + ':/tmp/dump.sql /tmp') - if r[0] == '': + print("同步文件", "start") + # cmd = 'scp -P' + str(master_port) + ' -i ' + SSH_PRIVATE_KEY + \ + # ' root@' + ip + ':/tmp/dump.sql.gz /tmp' + t = ssh.get_transport() + sftp = paramiko.SFTPClient.from_transport(t) + copy_status = sftp.get("/tmp/dump.sql.gz", "/tmp/dump.sql.gz") + print("同步信息:", copy_status) + print("同步文件", "end") + if copy_status == None: writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40}) - cmd = 'cd /www/server/mdserver-web && python /www/server/mdserver-web/plugins/mysql/index.py get_master_rep_slave_user_cmd {"username":"","db":""}' + cmd = 'cd /www/server/mdserver-web && python3 plugins/mariadb/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}' stdin, stdout, stderr = ssh.exec_command(cmd) result = stdout.read() - result_err = stderr.read() + result = result.decode('utf-8') cmd_data = json.loads(result) - # print(cmd_data) db.query('stop slave') writeDbSyncStatus({'code': 3, 'msg': '停止从库完成...', 'progress': 45}) - dlist = db.query(cmd_data['data']) + cmd = cmd_data['data']['cmd'] + # 保证同步IP一致 + if cmd.find('SOURCE_HOST') > -1: + cmd = re.sub(r"SOURCE_HOST='(.*)'", "SOURCE_HOST='" + ip + "'", cmd, 1) + + if cmd.find('MASTER_HOST') > -1: + cmd = re.sub(r"MASTER_HOST='(.*)'", "SOURCE_HOST='" + ip + "'", cmd, 1) + + db.query(cmd) + uinfo = cmd_data['data']['info'] + ps = uinfo['username'] + "|" + uinfo['password'] + id_rsa_conn.where('ip=?', (ip,)).setField('ps', ps) writeDbSyncStatus({'code': 4, 'msg': '刷新从库同步信息完成...', 'progress': 50}) pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') root_dir = getServerDir() msock = root_dir + "/mysql.sock" + mw.execShell("cd /tmp && gzip -d dump.sql.gz") cmd = root_dir + "/bin/mysql -S " + msock + \ " -uroot -p" + pwd + " < /tmp/dump.sql" import_data = mw.execShell(cmd) - print(import_data[0]) - print(import_data[1]) if import_data[0] == '': + print(import_data[1]) writeDbSyncStatus({'code': 5, 'msg': '导入数据完成...', 'progress': 90}) else: - writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 90}) + print(import_data[0]) + writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100}) return 'fail' - db.query('start slave') + db.query("start slave") writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) os.system("rm -rf " + SSH_PRIVATE_KEY) @@ -1898,7 +2132,7 @@ def fullSync(version=''): status_file = '/tmp/db_async_status.txt' if args['begin'] == '1': - cmd = 'cd ' + mw.getRunDir() + ' && python ' + \ + cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + \ getPluginDir() + \ '/index.py do_full_sync {"db":"' + args['db'] + '"} &' print(cmd) @@ -1909,8 +2143,10 @@ def fullSync(version=''): c = mw.readFile(status_file) tmp = json.loads(c) if tmp['code'] == 1: - dump_size = os.path.getsize("/tmp/dump.sql") - tmp['msg'] = tmp['msg'] + ":" + "同步文件:" + mw.toSize(dump_size) + 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: @@ -1926,12 +2162,18 @@ def installPreInspection(version): return "为了稳定安装MariaDB,先安装swap插件!" return 'ok' + +def uninstallPreInspection(version): + # return "请手动删除MySQL[{}]".format(version) + return 'ok' + if __name__ == "__main__": func = sys.argv[1] - version = "10.6" - if (len(sys.argv) > 2): - version = sys.argv[2] + version = "5.6" + version_pl = getServerDir() + "/version.pl" + if os.path.exists(version_pl): + version = mw.readFile(version_pl).strip() if func == 'status': print(status(version)) @@ -1949,6 +2191,8 @@ if __name__ == "__main__": print(initdInstall()) elif func == 'initd_uninstall': print(initdUinstall()) + elif func == 'install_pre_inspection': + print(installPreInspection(version)) elif func == 'uninstall_pre_inspection': print(uninstallPreInspection(version)) elif func == 'run_info': @@ -1961,6 +2205,8 @@ if __name__ == "__main__": print(getConf()) elif func == 'bin_log': print(binLog()) + elif func == 'clean_bin_log': + print(cleanBinLog()) elif func == 'error_log': print(getErrorLog()) elif func == 'show_log': @@ -1994,13 +2240,15 @@ if __name__ == "__main__": elif func == 'sync_to_databases': print(syncToDatabases()) elif func == 'set_root_pwd': - print(setRootPwd()) + print(setRootPwd(version)) elif func == 'set_user_pwd': - print(setUserPwd()) + print(setUserPwd(version)) elif func == 'get_db_access': print(getDbAccess()) elif func == 'set_db_access': print(setDbAccess()) + elif func == 'get_db_rw': + print(setDbRw(version)) elif func == 'set_db_ps': print(setDbPs()) elif func == 'get_db_info': @@ -2013,5 +2261,57 @@ if __name__ == "__main__": print(alterTable()) elif func == 'get_total_statistics': print(getTotalStatistics()) + elif func == 'get_dbrun_mode': + print(getDbrunMode(version)) + elif func == 'set_dbrun_mode': + print(setDbrunMode(version)) + 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 == 'set_db_slave': + print(setDbSlave(version)) + elif func == 'set_dbmaster_access': + print(setDbMasterAccess()) + 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 == 'update_master_rep_slave_user': + print(updateMasterRepSlaveUser(version)) + elif func == 'get_master_rep_slave_user_cmd': + print(getMasterRepSlaveUserCmd(version)) + elif func == 'get_slave_list': + print(getSlaveList(version)) + elif func == 'get_slave_sync_cmd': + print(getSlaveSyncCmd(version)) + elif func == 'get_slave_ssh_list': + print(getSlaveSSHList(version)) + elif func == 'get_slave_ssh_by_ip': + print(getSlaveSSHByIp(version)) + elif func == 'add_slave_ssh': + print(addSlaveSSH(version)) + elif func == 'del_slave_ssh': + print(delSlaveSSH(version)) + elif func == 'update_slave_ssh': + print(updateSlaveSSH(version)) + elif func == 'init_slave_status': + print(initSlaveStatus(version)) + elif func == 'set_slave_status': + print(setSlaveStatus(version)) + elif func == 'delete_slave': + print(deleteSlave(version)) + elif func == 'full_sync': + print(fullSync(version)) + elif func == 'do_full_sync': + print(doFullSync(version)) + elif func == 'dump_mysql_data': + print(dumpMysqlData(version)) else: print('error') diff --git a/plugins/mariadb/init.d/mariadb.tpl b/plugins/mariadb/init.d/mariadb.tpl old mode 100755 new mode 100644 index c14f5ad3a..6a0a54174 --- a/plugins/mariadb/init.d/mariadb.tpl +++ b/plugins/mariadb/init.d/mariadb.tpl @@ -1,4 +1,8 @@ #!/bin/sh +# chkconfig: 2345 55 25 +# Description: mysql service +# distro. For CentOS/Redhat run: 'chkconfig --add mysql' + # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB # This file is public domain and comes with NO WARRANTY of any kind @@ -25,7 +29,7 @@ # Description: MySQL is a very fast and reliable SQL database engine. ### END INIT INFO -# If you install MySQL on some other places than /Users/midoks/Desktop/fwww/server/mysql, then you +# If you install MySQL on some other places than /www/server/mysql, then you # have to do one of the following things for this script to work: # # - Run this script from within the MySQL installation directory @@ -110,7 +114,7 @@ mode=$1 # start or stop [ $# -ge 1 ] && shift -other_args="$*" # uncommon, but needed when called from an RPM upgrade action +other_args=--sql-mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" # uncommon, but needed when called from an RPM upgrade action # Expected: "--skip-networking --skip-grant-tables" # They are not checked here, intentionally, as it is the resposibility # of the "spec" file author to give correct arguments only. @@ -234,6 +238,11 @@ extra_args="" if test -r "$basedir/my.cnf" then extra_args="-e $basedir/my.cnf" +else + if test -r "$datadir/my.cnf" + then + extra_args="-e $datadir/my.cnf" + fi fi parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server` @@ -241,9 +250,10 @@ parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server m # # Set pid file if not given # +found_pid=`cd $datadir && ls |grep '.pid'` if test -z "$mysqld_pid_file_path" then - mysqld_pid_file_path=$datadir/`hostname`.pid + mysqld_pid_file_path=$datadir/$found_pid else case "$mysqld_pid_file_path" in /* ) ;; @@ -251,6 +261,7 @@ else esac fi +#ulimit -s unlimited case "$mode" in 'start') # Start daemon @@ -263,7 +274,7 @@ case "$mode" in then # Give extra arguments to mysqld with the my.cnf file. This script # may be overwritten at next upgrade. - $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null & + $bindir/mysqld_safe --defaults-file=$basedir/etc/my.cnf --datadir="$datadir" $other_args >/dev/null & wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$? # Make lock for RedHat / SuSE @@ -284,9 +295,6 @@ case "$mode" in if test -s "$mysqld_pid_file_path" then - # signal mysqld_safe that it needs to stop - touch "$mysqld_pid_file_path.shutdown" - mysqld_pid=`cat "$mysqld_pid_file_path"` if (kill -0 $mysqld_pid 2>/dev/null) @@ -345,7 +353,7 @@ case "$mode" in fi else # Try to find appropriate mysqld process - mysqld_pid=`pgrep -d' ' -f $libexecdir/mysqld` + mysqld_pid=`pidof $libexecdir/mysqld` # test if multiple pids exist pid_count=`echo $mysqld_pid | wc -w` diff --git a/plugins/mariadb/js/mariadb.js b/plugins/mariadb/js/mariadb.js index 6873ffe08..d501726bc 100755 --- a/plugins/mariadb/js/mariadb.js +++ b/plugins/mariadb/js/mariadb.js @@ -49,7 +49,7 @@ function myPostN(method,args,callback, title){ if (typeof(title) != 'undefined'){ _title = title; } - $.post('/plugins/run', {name:'mariadb', func:method, args:_args}, function(data) { + $.post('/plugins/run', {name:'mysql', func:method, args:_args}, function(data) { if(typeof(callback) == 'function'){ callback(data); } @@ -65,7 +65,7 @@ function myAsyncPost(method,args){ } var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); - return syncPost('/plugins/run', {name:'mariadb', func:method, args:_args}); + return syncPost('/plugins/run', {name:'mysql', func:method, args:_args}); } function runInfo(){ @@ -526,6 +526,17 @@ function checkSelect(){ },5) } +function setDbRw(id,username,val){ + myPost('get_db_rw',{id:id,username:username,rw:val}, function(data){ + var rdata = $.parseJSON(data.data); + // layer.msg(rdata.msg,{icon:rdata.status ? 1 : 5,shade: [0.3, '#000']}); + showMsg(rdata.msg, function(){ + dbList(); + },{icon:rdata.status ? 1 : 5,shade: [0.3, '#000']}, 2000); + + }); +} + function setDbAccess(username){ myPost('get_db_access','username='+username, function(data){ var rdata = $.parseJSON(data.data); @@ -540,8 +551,9 @@ function setDbAccess(username){ title: '设置数据库权限', closeBtn: 1, shift: 5, + btn:["提交","取消"], shadeClose: true, - content: "
\ + content: "\
\ 访问权限\
\ @@ -552,65 +564,54 @@ function setDbAccess(username){ \
\
\ -
\ - \ - \ -
\
", - }); - - layer.ready(function(){ - if (rdata.msg == '127.0.0.1'){ - $('select[name="dataAccess"]').find("option[value='127.0.0.1']").attr("selected",true); - } else if (rdata.msg == '%'){ - $('select[name="dataAccess"]').find('option[value="%"]').attr("selected",true); - } else if ( rdata.msg == 'ip' ){ - $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); - $('select[name="dataAccess"]').after(""); - } else { - $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); - $('select[name="dataAccess"]').after(""); - } - }); - - $('#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(); - } - }); + success:function(){ + if (rdata.msg == '127.0.0.1'){ + $('select[name="dataAccess"]').find("option[value='127.0.0.1']").attr("selected",true); + } else if (rdata.msg == '%'){ + $('select[name="dataAccess"]').find('option[value="%"]').attr("selected",true); + } else if ( rdata.msg == 'ip' ){ + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } else { + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } - $('#my_mod_save').click(function(){ - var data = $("#set_db_access").serialize(); - data = decodeURIComponent(data); - var dataObj = str2Obj(data); - if(!dataObj['access']){ - dataObj['access'] = dataObj['dataAccess']; - if ( dataObj['dataAccess'] == 'ip'){ - if (dataObj['address']==''){ - layer.msg('IP地址不能空!',{icon:2,shade: [0.3, '#000']}); - return; + $('select[name="dataAccess"]').change(function(){ + var v = $(this).val(); + if (v == 'ip'){ + $(this).after(""); + } else { + $('#dataAccess_subid').remove(); + } + }); + }, + yes:function(index){ + var data = $("#set_db_access").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['access']){ + dataObj['access'] = dataObj['dataAccess']; + if ( dataObj['dataAccess'] == 'ip'){ + if (dataObj['address']==''){ + layer.msg('IP地址不能空!',{icon:2,shade: [0.3, '#000']}); + return; + } + dataObj['access'] = dataObj['address']; } - dataObj['access'] = dataObj['address']; } + dataObj['username'] = username; + myPost('set_db_access', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + dbList(); + },{icon: rdata.status ? 1 : 2}); + }); } - dataObj['username'] = username; - // console.log(data,dataObj); - myPost('set_db_access', dataObj, function(data){ - var rdata = $.parseJSON(data.data); - showMsg(rdata.msg,function(){ - dbList(); - $('.layui-layer-close1').click(); - },{icon: rdata.status ? 1 : 2}); - }); }); + }); } @@ -618,13 +619,13 @@ function setDbPass(id, username, password){ var index = layer.open({ type: 1, - skin: 'demo-class', area: '500px', title: '修改数据库密码', closeBtn: 1, shift: 5, shadeClose: true, - content: "
\ + btn:["提交","关闭"], + content: "\
\ 用户名\
\ @@ -634,26 +635,17 @@ function setDbPass(id, username, password){
\
\ \ -
\ - \ - \ -
\
", - }); - - $('#my_mod_close').click(function(){ - $('.layui-layer-close1').click(); - }); - - $('#my_mod_save').click(function(){ - var data = $("#mod_pwd").serialize(); - myPost('set_user_pwd', data, function(data){ - var rdata = $.parseJSON(data.data); - showMsg(rdata.msg,function(){ - dbList(); - $('.layui-layer-close1').click(); - },{icon: rdata.status ? 1 : 2}); - }); + yes:function(index){ + var data = $("#mod_pwd").serialize(); + myPost('set_user_pwd', data, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + dbList(); + },{icon: rdata.status ? 1 : 2}); + }); + } }); } @@ -835,7 +827,7 @@ function openPhpmyadmin(name,username,password){ //检查版本 data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'version'}); bigVer = data.data.split('.')[0] - if (bigVer>=5){ + if (bigVer>=4.5){ setTimeout(function(){ $("#toPHPMyAdmin").submit(); @@ -968,9 +960,28 @@ function dbList(page, search){ list += ''+(rdata.data[i]['is_backup']?'备份':'未备份') +' | '; + var rw = ''; + var rw_change = 'all'; + if (typeof(rdata.data[i]['rw'])!='undefined'){ + var rw_val = '读写'; + if (rdata.data[i]['rw'] == 'all'){ + rw_val = "所有"; + rw_change = 'rw'; + } else if (rdata.data[i]['rw'] == 'rw'){ + rw_val = "读写"; + rw_change = 'r'; + } else if (rdata.data[i]['rw'] == 'r'){ + rw_val = "只读"; + rw_change = 'all'; + } + rw = ''+rw_val+' | '; + } + + list += '管理 | ' + '工具 | ' + '权限 | ' + + rw + '改密 | ' + '删除' + ''; @@ -1001,8 +1012,8 @@ function dbList(page, search){ '+ list +'\ \
\ -
\ -
\ +
\ +
\ 同步选中\ 同步所有\ 从服务器获取\ @@ -1031,12 +1042,20 @@ function myLogs(){ myPost('bin_log', {status:1}, function(data){ var rdata = $.parseJSON(data.data); + var line_status = "" + if (rdata.status){ + line_status = '\ + '; + } else { + line_status = ''; + } + var limitCon = '

\ 二进制日志 ' + toSize(rdata.msg) + '\ - \ -

错误日志

\ + '+line_status+'\ +

错误日志

\ \ -

' +

'; $(".soft-man-con").html(limitCon); //设置二进制日志 @@ -1044,10 +1063,15 @@ function myLogs(){ myPost('bin_log', 'close=change', function(data){ var rdata = $.parseJSON(data.data); layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); - - setTimeout(function(){ - myLogs(); - }, 2000); + setTimeout(function(){myLogs();}, 2000); + }); + }); + + $(".clean-btn-bin").click(function () { + myPost('clean_bin_log', '', function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){myLogs();}, 2000); }); }); @@ -1056,10 +1080,7 @@ function myLogs(){ myPost('error_log', 'close=1', function(data){ var rdata = $.parseJSON(data.data); layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); - - setTimeout(function(){ - myLogs(); - }, 2000); + setTimeout(function(){myLogs();}, 2000); }); }) @@ -1071,7 +1092,7 @@ function myLogs(){ } else { error_body = rdata.msg; } - $("#error_log").text(error_body); + $("#error_log").html(error_body); var ob = document.getElementById('error_log'); ob.scrollTop = ob.scrollHeight; }); @@ -1181,7 +1202,7 @@ function repTools(db_name, res){ layer.open({ type: 1, - title: "MySQL工具箱【" + db_name + "】", + title: "MariaDB工具箱【" + db_name + "】", area: ['780px', '580px'], closeBtn: 2, shadeClose: false, @@ -1219,3 +1240,796 @@ function repTools(db_name, res){ }); } + +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 setDbSlave(name){ + myPost('set_db_slave', {name:name}, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){ + masterOrSlaveConf(); + }, 2000); + }); +} + + +function addMasterRepSlaveUser(){ + layer.open({ + type: 1, + area: '500px', + title: '添加同步账户', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["提交","取消"], + content: "
\ +
用户名
\ +
\ + 密码\ +
\ +
\ + \ +
", + success:function(){ + $("input[name='name']").keyup(function(){ + var v = $(this).val(); + $("input[name='db_user']").val(v); + $("input[name='ps']").val(v); + }); + + $('select[name="dataAccess"]').change(function(){ + var v = $(this).val(); + if (v == 'ip'){ + $(this).after(""); + } else { + $('#dataAccess_subid').remove(); + } + }); + }, + yes:function(index){ + var data = $("#add_master").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['address']){ + dataObj['address'] = dataObj['dataAccess']; + } + + myPost('add_master_rep_slave_user', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + if (rdata.status){ + getMasterRepSlaveList(); + } + },{icon: rdata.status ? 1 : 2},600); + }); + } + }); +} + + + +function updateMasterRepSlaveUser(username){ + + var index = layer.open({ + type: 1, + area: '500px', + title: '更新账户', + closeBtn: 1, + shift: 5, + shadeClose: true, + content: "
\ +
用户名
\ +
\ + 密码\ +
\ +
\ + \ +
\ + \ +
\ +
", + }); + + $('#submit_update_master').click(function(){ + var data = $("#update_master").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + myPost('update_master_rep_slave_user', data, 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, db=''){ + myPost('get_master_rep_slave_user_cmd', {username:username,db:db}, function(data){ + var rdata = $.parseJSON(data.data); + + if (!rdata['status']){ + layer.msg(rdata['msg']); + return; + } + + var cmd = rdata.data['cmd']; + + var loadOpen = layer.open({ + type: 1, + title: '同步命令', + area: '500px', + content:"
\ +
"+cmd+"
\ +
\ + \ +
\ +
", + }); + + + copyPass(cmd); + $('.class-copy-cmd').click(function(){ + copyPass(cmd); + }); + }); +} + +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 setDbMasterAccess(username){ + myPost('get_db_access','username='+username, function(data){ + var rdata = $.parseJSON(data.data); + if (!rdata.status){ + layer.msg(rdata.msg,{icon:2,shade: [0.3, '#000']}); + return; + } + + var index = layer.open({ + type: 1, + area: '500px', + title: '设置数据库权限', + closeBtn: 1, + shift: 5, + btn:["提交","取消"], + shadeClose: true, + content: "
\ +
\ + 访问权限\ +
\ + \ +
\ +
\ +
", + success:function(){ + if (rdata.msg == '127.0.0.1'){ + $('select[name="dataAccess"]').find("option[value='127.0.0.1']").attr("selected",true); + } else if (rdata.msg == '%'){ + $('select[name="dataAccess"]').find('option[value="%"]').attr("selected",true); + } else if ( rdata.msg == 'ip' ){ + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } else { + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } + + $('select[name="dataAccess"]').change(function(){ + var v = $(this).val(); + if (v == 'ip'){ + $(this).after(""); + } else { + $('#dataAccess_subid').remove(); + } + }); + }, + yes:function(index){ + var data = $("#set_db_access").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['access']){ + dataObj['access'] = dataObj['dataAccess']; + if ( dataObj['dataAccess'] == 'ip'){ + if (dataObj['address']==''){ + layer.msg('IP地址不能空!',{icon:2,shade: [0.3, '#000']}); + return; + } + dataObj['access'] = dataObj['address']; + } + } + dataObj['username'] = username; + myPost('set_dbmaster_access', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + },{icon: rdata.status ? 1 : 2}); + }); + } + }); + + }); +} + +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']+'\ + \ + 修改 | \ + 删除 | \ + 权限 | \ + 从库同步命令\ + \ + '; + } + + $('#get_master_rep_slave_list_page tbody').html(list); + $('.dataTables_paginate_4').html(rdata['page']); + }); +} + +function getMasterRepSlaveListPage(){ + var page = '
'; + page += '
添加同步账户
'; + + var loadOpen = layer.open({ + type: 1, + title: '同步账户列表', + area: '500px', + content:"
\ +
\ +
\ + \ + \ +
用户名密码操作
\ + "+page +"\ +
\ +
", + success:function(){ + getMasterRepSlaveList(); + } + }); +} + + +function deleteSlave(){ + myPost('delete_slave', {}, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata['msg'], function(){ + masterOrSlaveConf(); + },{},3000); + }); +} + + +function getFullSyncStatus(db){ + var timeId = null; + + var btn = '
开始
'; + var loadOpen = layer.open({ + type: 1, + title: '全量同步['+db+']', + area: '500px', + content:"
\ +
\ + \ +
\ +
0%
\ +
\ +
\ + "+btn+"\ +
", + cancel: function(){ + clearInterval(timeId); + } + }); + + function fullSync(db,begin){ + + myPostN('full_sync', {db:db,begin:begin}, function(data){ + var rdata = $.parseJSON(data.data); + $('#full_msg').text(rdata['msg']); + $('.progress-bar').css('width',rdata['progress']+'%'); + $('.progress-bar').text(rdata['progress']+'%'); + + if (rdata['code']==6 ||rdata['code']<0){ + layer.msg(rdata['msg']); + clearInterval(timeId); + $("#begin_full_sync").attr('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=''){ + + myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){ + + var rdata = $.parseJSON(rdata.data); + + var ip = '127.0.0.1'; + var port = "22"; + var id_rsa = ''; + var db_user =''; + + if (rdata.data.length>0){ + ip = rdata.data[0]['ip']; + port = rdata.data[0]['port']; + id_rsa = rdata.data[0]['id_rsa']; + db_user = rdata.data[0]['db_user']; + } + + var index = layer.open({ + type: 1, + area: ['500px','480px'], + title: '添加SSH', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["确认","取消"], + content: "
\ +
IP
\ +
端口
\ +
同步账户[DB]
\ +
\ + ID_RSA\ +
\ +
\ + \ +
", + success:function(){ + $('textarea[name="id_rsa"]').html(id_rsa); + }, + yes:function(index){ + var ip = $('input[name="ip"]').val(); + var port = $('input[name="port"]').val(); + var db_user = $('input[name="db_user"]').val(); + var id_rsa = $('textarea[name="id_rsa"]').val(); + + var data = {ip:ip,port:port,id_rsa:id_rsa,db_user:db_user}; + myPost('add_slave_ssh', data, function(data){ + layer.close(index); + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + if (rdata.status){ + getSlaveSSHPage(); + } + },{icon: rdata.status ? 1 : 2},600); + }); + } + }); + }); +} + + +function delSlaveSSH(ip){ + myPost('del_slave_ssh', {ip:ip}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + getSlaveSSHPage(); + }); +} + +function getSlaveSSHPage(page=1){ + var _data = {}; + _data['page'] = page; + _data['page_size'] = 5; + _data['tojs'] ='getSlaveSSHPage'; + myPost('get_slave_ssh_list', _data, function(data){ + var layerId = null; + var rdata = []; + try { + rdata = $.parseJSON(data.data); + } catch(e) { + console.log(e); + } + var list = ''; + var ssh_list = rdata['data']; + for (i in ssh_list) { + var ip = ssh_list[i]['ip']; + var port = ssh_list[i]['port']; + + var id_rsa = '未设置'; + if ( ssh_list[i]['port'] != ''){ + id_rsa = '已设置'; + } + + var db_user = '未设置'; + if ( ssh_list[i]['db_user'] != ''){ + db_user = ssh_list[i]['db_user']; + } + + list += ''+ip+'\ + '+port+'\ + '+db_user+'\ + '+id_rsa+'\ + \ + 修改 | \ + 删除\ + \ + '; + } + + $('.get-slave-ssh-list tbody').html(list); + $('.dataTables_paginate_4').html(rdata['page']); + }); +} + + +function getSlaveSSHList(page=1){ + + var page = '
'; + page += '
添加SSH
'; + + layerId = layer.open({ + type: 1, + title: 'SSH列表', + area: '500px', + content:"
\ +
\ +
\ + \ + \ +
IPPORT同步账户SSH操作
\ + "+page +"\ +
\ +
", + success:function(){ + getSlaveSSHPage(1); + } + }); +} + +function handlerRun(){ + myPostN('get_slave_sync_cmd', {}, function(data){ + var rdata = $.parseJSON(data.data); + var cmd = rdata['data']; + var loadOpen = layer.open({ + type: 1, + title: '手动执行', + area: '500px', + content:"
\ +
"+cmd+"
\ +
\ + \ +
\ +
", + }); + copyPass(cmd); + $('.class-copy-cmd').click(function(){ + copyPass(cmd); + }); + }); +} + +function initSlaveStatus(){ + myPost('init_slave_status', '', function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + if (rdata.status){ + masterOrSlaveConf(); + } + },{icon:rdata.status?1:2},2000); + }); +} + +function masterOrSlaveConf(version=''){ + + function getMasterDbList(){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + + myPost('get_masterdb_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + var list = ''; + for(i in rdata.data){ + list += ''; + list += '' + rdata.data[i]['name'] +''; + list += '' + (rdata.data[i]['master']?'是':'否') +''; + list += '' + + ''+(rdata.data[i]['master']?'退出':'加入')+' | ' + + '同步命令' + + ''; + list += ''; + } + + var con = '
\ +
\ + \ + \ + \ + \ + \ + \ + '+ list +'\ +
数据库名同步操作
\ +
\ +
\ +
\ + 同步账户列表\ +
\ +
'; + + $(".table_master_list").html(con); + $('#databasePage').html(rdata.page); + }); + } + + function getAsyncMasterDbList(){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + + myPost('get_slave_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + var list = ''; + for(i in rdata.data){ + + var v = rdata.data[i]; + var status = "异常"; + if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ + status = "正常"; + } + + list += ''; + list += '' + rdata.data[i]['Master_Host'] +''; + list += '' + rdata.data[i]['Master_Port'] +''; + list += '' + rdata.data[i]['Master_User'] +''; + list += '' + rdata.data[i]['Master_Log_File'] +''; + list += '' + rdata.data[i]['Slave_IO_Running'] +''; + list += '' + rdata.data[i]['Slave_SQL_Running'] +''; + list += '' + status +''; + list += '' + + '删除' + + ''; + list += ''; + } + + var con = '
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + '+ list +'\ +
主[服务]端口用户日志IOSQL状态操作
\ +
\ +
'; + + //
\ + //
\ + // 添加\ + //
+ $(".table_slave_status_list").html(con); + }); + } + + function getAsyncDataList(){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + myPost('get_masterdb_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + var list = ''; + for(i in rdata.data){ + list += ''; + list += '' + rdata.data[i]['name'] +''; + list += '' + + ''+(rdata.data[i]['slave']?'退出':'加入')+' | ' + + '同步' + + ''; + list += ''; + } + + var con = '
\ +
\ + \ + \ + \ + \ + \ + '+ list +'\ +
本地库名操作
\ +
\ +
\ +
\ + 手动命令\ + 全量同步\ +
\ +
'; + + $(".table_slave_list").html(con); + $('#databasePage').html(rdata.page); + }); + } + + + + function getMasterStatus(){ + myPost('get_master_status', '', function(data){ + var rdata = $.parseJSON(data.data); + // console.log('mode:',rdata.data); + var rdata = rdata.data; + var limitCon = '\ +

\ + 主从同步模式\ + \ + \ +

\ +
\ +

\ + Master[主]配置\ + \ +

\ +
\ + \ +
\ +
\ + \ +

\ + Slave[从]配置\ + \ + \ + \ +

\ +
\ + \ +
\ + \ +
\ + '; + $(".soft-man-con").html(limitCon); + + //设置主服务器配置 + $(".btn-master").click(function () { + myPost('set_master_status', 'close=change', function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){ + getMasterStatus(); + }, 3000); + }); + }); + + $(".btn-slave").click(function () { + myPost('set_slave_status', 'close=change', function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){ + getMasterStatus(); + }, 3000); + }); + }); + + $('.db-mode').click(function(){ + if ($(this).hasClass('btn-success')){ + //no action + return; + } + + var mode = 'classic'; + if ($(this).hasClass('btn-gtid')){ + mode = 'gtid'; + } + + layer.open({ + type:1, + title:"MySQL主从模式切换", + shadeClose:false, + btnAlign: 'c', + btn: ['切换并重启', '切换不重启'], + yes: function(index, layero){ + this.change(index,mode,"yes"); + + }, + btn2: function(index, layero){ + this.change(index,mode,"no"); + return false; + }, + change:function(index,mode,reload){ + console.log(index,mode,reload); + myPost('set_dbrun_mode',{'mode':mode,'reload':reload},function(data){ + layer.close(index); + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg ,function(){ + getMasterStatus(); + },{ icon: rdata.status ? 1 : 5 }); + }); + } + }); + }); + + if (rdata.status){ + getMasterDbList(); + } + + if (rdata.slave_status){ + getAsyncMasterDbList(); + getAsyncDataList() + } + }); + } + getMasterStatus(); +} diff --git a/plugins/mariadb/scripts/backup.py b/plugins/mariadb/scripts/backup.py new file mode 100755 index 000000000..21ef91d10 --- /dev/null +++ b/plugins/mariadb/scripts/backup.py @@ -0,0 +1,125 @@ +# coding: utf-8 +#----------------------------- +# 网站备份工具 +#----------------------------- + +import sys +import os + +if sys.platform != 'darwin': + os.chdir('/www/server/mdserver-web') + + +chdir = os.getcwd() +sys.path.append(chdir + '/class/core') + +# reload(sys) +# sys.setdefaultencoding('utf-8') + + +import mw +import db +import time + + +class backupTools: + + def backupDatabase(self, name, count): + db_path = mw.getServerDir() + '/mariadb' + db_name = 'mysql' + name = mw.M('databases').dbPos(db_path, 'mysql').where( + 'name=?', (name,)).getField('name') + startTime = time.time() + if not name: + endDate = time.strftime('%Y/%m/%d %X', time.localtime()) + log = "数据库[" + name + "]不存在!" + print("★[" + endDate + "] " + log) + print( + "----------------------------------------------------------------------------") + return + + backup_path = mw.getRootDir() + '/backup/database' + if not os.path.exists(backup_path): + mw.execShell("mkdir -p " + backup_path) + + filename = backup_path + "/db_" + name + "_" + \ + time.strftime('%Y%m%d_%H%M%S', time.localtime()) + ".sql.gz" + + import re + mysql_root = mw.M('config').dbPos(db_path, db_name).where( + "id=?", (1,)).getField('mysql_root') + + mycnf = mw.readFile(db_path + '/etc/my.cnf') + rep = "\[mysqldump\]\nuser=root" + sea = "[mysqldump]\n" + subStr = sea + "user=root\npassword=" + mysql_root + "\n" + mycnf = mycnf.replace(sea, subStr) + if len(mycnf) > 100: + mw.writeFile(db_path + '/etc/my.cnf', mycnf) + + # mw.execShell(db_path + "/bin/mysqldump --opt --default-character-set=utf8 " + + # name + " | gzip > " + filename) + + # mw.execShell(db_path + "/bin/mysqldump --skip-lock-tables --default-character-set=utf8 " + + # name + " | gzip > " + filename) + + mw.execShell(db_path + "/bin/mysqldump --single-transaction --quick --default-character-set=utf8 " + + name + " | gzip > " + filename) + + if not os.path.exists(filename): + endDate = time.strftime('%Y/%m/%d %X', time.localtime()) + log = "数据库[" + name + "]备份失败!" + print("★[" + endDate + "] " + log) + print( + "----------------------------------------------------------------------------") + return + + mycnf = mw.readFile(db_path + '/etc/my.cnf') + mycnf = mycnf.replace(subStr, sea) + if len(mycnf) > 100: + mw.writeFile(db_path + '/etc/my.cnf', mycnf) + + endDate = time.strftime('%Y/%m/%d %X', time.localtime()) + outTime = time.time() - startTime + pid = mw.M('databases').dbPos(db_path, db_name).where( + 'name=?', (name,)).getField('id') + + mw.M('backup').add('type,name,pid,filename,addtime,size', (1, os.path.basename( + filename), pid, filename, endDate, os.path.getsize(filename))) + log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒" + mw.writeLog('计划任务', log) + print("★[" + endDate + "] " + log) + print("|---保留最新的[" + count + "]份备份") + print("|---文件名:" + filename) + + # 清理多余备份 + backups = mw.M('backup').where( + 'type=? and pid=?', ('1', pid)).field('id,filename').select() + + num = len(backups) - int(count) + if num > 0: + for backup in backups: + mw.execShell("rm -f " + backup['filename']) + mw.M('backup').where('id=?', (backup['id'],)).delete() + num -= 1 + print("|---已清理过期备份文件:" + backup['filename']) + if num < 1: + break + + def backupDatabaseAll(self, save): + db_path = mw.getServerDir() + '/mariadb' + db_name = 'mysql' + databases = mw.M('databases').dbPos( + db_path, db_name).field('name').select() + for database in databases: + self.backupDatabase(database['name'], save) + + +if __name__ == "__main__": + backup = backupTools() + type = sys.argv[1] + if type == 'database': + if sys.argv[2] == 'ALL': + backup.backupDatabaseAll(sys.argv[3]) + else: + backup.backupDatabase(sys.argv[2], sys.argv[3]) diff --git a/plugins/mariadb/versions/10.6/install.sh b/plugins/mariadb/versions/10.6/install.sh index 70075fd69..8a0f55cf4 100755 --- a/plugins/mariadb/versions/10.6/install.sh +++ b/plugins/mariadb/versions/10.6/install.sh @@ -51,6 +51,10 @@ Install_app() else cpuCore="1" fi + + if [ "$cpuCore" -gt "1" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + fi # ----- cpu end ------ if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then @@ -63,6 +67,11 @@ Install_app() cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz fi + OPTIONS='' + if [ "$sysName" == "Darwin" ];then + OPTIONS='-DPLUGIN_TOKUDB=NO' + fi + if [ ! -d $serverPath/mariadb ];then cd ${mariadbDir}/mariadb-${MY_VER} && cmake \ @@ -75,6 +84,7 @@ Install_app() -DWITH_MEMORY_STORAGE_ENGINE=1 \ -DENABLED_LOCAL_INFILE=1 \ -DWITH_PARTITION_STORAGE_ENGINE=1 \ + $OPTIONS \ -DEXTRA_CHARSETS=all \ -DDEFAULT_CHARSET=utf8mb4 \ -DDEFAULT_COLLATION=utf8mb4_general_ci \ diff --git a/plugins/mariadb/versions/10.7/install.sh b/plugins/mariadb/versions/10.7/install.sh index b7a157592..ab3508dab 100755 --- a/plugins/mariadb/versions/10.7/install.sh +++ b/plugins/mariadb/versions/10.7/install.sh @@ -51,6 +51,10 @@ Install_app() else cpuCore="1" fi + + if [ "$cpuCore" -gt "1" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + fi # ----- cpu end ------ if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then diff --git a/plugins/mariadb/versions/10.8/install.sh b/plugins/mariadb/versions/10.8/install.sh index b3bee2bdc..8321b1fad 100755 --- a/plugins/mariadb/versions/10.8/install.sh +++ b/plugins/mariadb/versions/10.8/install.sh @@ -51,6 +51,10 @@ Install_app() else cpuCore="1" fi + + if [ "$cpuCore" -gt "1" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + fi # ----- cpu end ------ if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then diff --git a/plugins/mysql/conf/classic.cnf b/plugins/mysql/conf/classic.cnf new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/mysql/conf/gtid.cnf b/plugins/mysql/conf/gtid.cnf new file mode 100644 index 000000000..5076776a6 --- /dev/null +++ b/plugins/mysql/conf/gtid.cnf @@ -0,0 +1,4 @@ +[mysqld] +# SHOW GLOBAL VARIABLES LIKE '%gtid%' +gtid_mode=ON +enforce_gtid_consistency=ON \ No newline at end of file diff --git a/plugins/mysql/conf/my.cnf b/plugins/mysql/conf/my.cnf index 369e55b28..48c5a89f6 100644 --- a/plugins/mysql/conf/my.cnf +++ b/plugins/mysql/conf/my.cnf @@ -5,6 +5,11 @@ port = 3306 socket = {$SERVER_APP_PATH}/mysql.sock [mysqld] +!include {$SERVER_APP_PATH}/etc/mode/classic.cnf + +sha256_password_private_key_path=mysql.pem +sha256_password_public_key_path=mysql.pub + pid-file = {$SERVER_APP_PATH}/data/mysql.pid user = mysql port = 3306 @@ -12,9 +17,13 @@ socket = {$SERVER_APP_PATH}/mysql.sock basedir = {$SERVER_APP_PATH} datadir = {$SERVER_APP_PATH}/data log-error = {$SERVER_APP_PATH}/data/error.log -default_storage_engine = MyISAM +server-id = {$SERVER_ID} + +default_storage_engine = InnoDB + key_buffer_size = 8M max_allowed_packet = 100M + table_open_cache = 32 sort_buffer_size = 256K net_buffer_length = 4K @@ -22,7 +31,7 @@ read_buffer_size = 128K read_rnd_buffer_size = 256K myisam_sort_buffer_size = 4M thread_cache_size = 4 -lower_case_table_names=1 +lower_case_table_names=0 query_cache_size = 4M tmp_table_size = 8M @@ -30,22 +39,21 @@ max_connections = 500 max_connect_errors = 100 open_files_limit = 65535 +skip-name-resolve=1 +#skip-grant-tables #skip-networking -#skip-name-resolve #skip-external-locking #loose-skip-innodb -#skip-grant-tables -skip-ssl - log-bin=mysql-bin binlog_format=mixed -server-id = 1 -expire_logs_days = 10 slow_query_log=1 slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log -long_query_time=3 +long_query_time=10 #log_queries_not_using_indexes=on +#log_slow_admin_statements=1 +#log_slow_slave_statements=1 +expire_logs_days=30 relay-log=mdserver relay-log-index=mdserver @@ -58,14 +66,15 @@ binlog-ignore-db = information_schema binlog-ignore-db = performance_schema #slave -log-slave-updates +log-slave-updates = 1 +skip-slave-start = 1 #replicate-do-db replicate-ignore-db = information_schema replicate-ignore-db = performance_schema replicate-ignore-db = mysql replicate-ignore-db = test -default_storage_engine = InnoDB + innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_file_path = ibdata1:10M:autoextend innodb_log_group_home_dir = {$SERVER_APP_PATH}/data @@ -73,12 +82,13 @@ innodb_buffer_pool_size = 16M innodb_additional_mem_pool_size = 2M innodb_log_file_size = 5M innodb_log_buffer_size = 8M -innodb_flush_log_at_trx_commit = 1 +innodb_flush_log_at_trx_commit = 2 innodb_lock_wait_timeout = 120 innodb_max_dirty_pages_pct = 90 innodb_read_io_threads = 1 innodb_write_io_threads = 1 innodb_file_per_table=1 +innodb_large_prefix = 1 secure-file-priv={$SERVER_APP_PATH}/tmp diff --git a/plugins/mysql/conf/my5.7.cnf b/plugins/mysql/conf/my5.7.cnf index 5053013b6..df79938ef 100644 --- a/plugins/mysql/conf/my5.7.cnf +++ b/plugins/mysql/conf/my5.7.cnf @@ -6,6 +6,11 @@ socket = {$SERVER_APP_PATH}/mysql.sock default-character-set = UTF8MB4 [mysqld] +!include {$SERVER_APP_PATH}/etc/mode/classic.cnf + +;sha256_password_private_key_path=mysql.pem +;sha256_password_public_key_path=mysql.pub + pid-file = {$SERVER_APP_PATH}/data/mysql.pid user = mysql port = 3306 @@ -13,7 +18,9 @@ socket = {$SERVER_APP_PATH}/mysql.sock basedir = {$SERVER_APP_PATH} datadir = {$SERVER_APP_PATH}/data log-error = {$SERVER_APP_PATH}/data/error.log -default_storage_engine = MyISAM +server-id = {$SERVER_ID} + +default_storage_engine = InnoDB key_buffer_size = 8M table_open_cache = 32 @@ -32,20 +39,21 @@ max_connect_errors = 100 open_files_limit = 2560 max_allowed_packet = 128M +skip_name_resolve = 1 +#skip-networking #skip-external-locking -#skip-grant-tables #loose-skip-innodb -#skip-networking -#skip-name-resolve -skip-ssl +#skip-grant-tables log-bin=mysql-bin binlog_format=mixed -server-id = 1 slow_query_log=1 slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log -long_query_time=3 -#log_queries_not_using_indexes=on +long_query_time=10 +#log_queries_not_using_indexes=1 +#log_slow_admin_statements=1 +#log_slow_slave_statements=1 +expire_logs_days=30 relay-log=mdserver relay-log-index=mdserver @@ -58,26 +66,28 @@ binlog-ignore-db = information_schema binlog-ignore-db = performance_schema #slave -log-slave-updates +log-slave-updates = 1 +skip-slave-start = 1 #replicate-do-db replicate-ignore-db = information_schema replicate-ignore-db = performance_schema replicate-ignore-db = mysql replicate-ignore-db = test -default_storage_engine = InnoDB + innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_file_path = ibdata1:10M:autoextend innodb_log_group_home_dir = {$SERVER_APP_PATH}/data innodb_buffer_pool_size = 16M innodb_log_file_size = 5M innodb_log_buffer_size = 8M -innodb_flush_log_at_trx_commit = 1 +innodb_flush_log_at_trx_commit = 2 innodb_lock_wait_timeout = 120 innodb_max_dirty_pages_pct = 90 innodb_read_io_threads = 1 innodb_write_io_threads = 1 -innodb_file_per_table=1 +innodb_file_per_table = 1 +innodb_large_prefix = 1 secure-file-priv={$SERVER_APP_PATH}/tmp diff --git a/plugins/mysql/conf/my8.0.cnf b/plugins/mysql/conf/my8.0.cnf index e1e82a44a..4dbaaab0a 100644 --- a/plugins/mysql/conf/my8.0.cnf +++ b/plugins/mysql/conf/my8.0.cnf @@ -6,7 +6,12 @@ socket = {$SERVER_APP_PATH}/mysql.sock default-character-set = UTF8MB4 [mysqld] -default_authentication_plugin=mysql_native_password +!include {$SERVER_APP_PATH}/etc/mode/classic.cnf + +sha256_password_private_key_path=mysql.pem +sha256_password_public_key_path=mysql.pub +authentication_policy=mysql_native_password + pid-file = {$SERVER_APP_PATH}/data/mysql.pid user = mysql port = 3306 @@ -14,7 +19,9 @@ socket = {$SERVER_APP_PATH}/mysql.sock basedir = {$SERVER_APP_PATH} datadir = {$SERVER_APP_PATH}/data log-error = {$SERVER_APP_PATH}/data/error.log -default_storage_engine = MyISAM +server-id = {$SERVER_ID} + +default_storage_engine = InnoDB key_buffer_size = 8M table_open_cache = 32 @@ -33,21 +40,21 @@ max_connect_errors = 100 open_files_limit = 2560 max_allowed_packet = 128M +skip_name_resolve = 1 +#skip-networking #skip-external-locking -#skip-grant-tables #loose-skip-innodb -#skip-networking -#skip-name-resolve -skip-ssl - +#skip-grant-tables log-bin=mysql-bin binlog_format=mixed -server-id = 1 slow_query_log=1 slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log -long_query_time=3 -#log_queries_not_using_indexes=on +long_query_time=10 +#log_queries_not_using_indexes=1 +#log_slow_admin_statements=1 +#log_slow_replica_statements=1 +binlog_expire_logs_seconds=2592000 relay-log=mdserver relay-log-index=mdserver @@ -60,27 +67,27 @@ binlog-ignore-db = information_schema binlog-ignore-db = performance_schema #slave -log_replica_updates +log_replica_updates = 1 +skip_replica_start = 1 #replicate-do-db replicate-ignore-db = information_schema replicate-ignore-db = performance_schema replicate-ignore-db = mysql replicate-ignore-db = test -default_storage_engine = InnoDB + innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_file_path = ibdata1:10M:autoextend innodb_log_group_home_dir = {$SERVER_APP_PATH}/data innodb_buffer_pool_size = 16M innodb_log_file_size = 5M innodb_log_buffer_size = 8M -innodb_flush_log_at_trx_commit = 1 +innodb_flush_log_at_trx_commit = 2 innodb_lock_wait_timeout = 120 innodb_max_dirty_pages_pct = 90 innodb_read_io_threads = 1 innodb_write_io_threads = 1 innodb_file_per_table=1 -binlog_expire_logs_seconds=2592000 secure-file-priv={$SERVER_APP_PATH}/tmp diff --git a/plugins/mysql/conf/mysql.sql b/plugins/mysql/conf/mysql.sql index c62fc5c94..f6df3e957 100755 --- a/plugins/mysql/conf/mysql.sql +++ b/plugins/mysql/conf/mysql.sql @@ -12,9 +12,11 @@ CREATE TABLE IF NOT EXISTS `databases` ( `username` TEXT, `password` TEXT, `accept` TEXT, + `rw` TEXT DEFAULT 'rw', `ps` TEXT, `addtime` TEXT ); +-- ALTER TABLE `databases` ADD COLUMN `rw` TEXT DEFAULT 'rw'; CREATE TABLE IF NOT EXISTS `master_replication_user` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -32,6 +34,7 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` ( `ip` TEXT, `port` TEXT, `user` TEXT, + `db_user` TEXT, `id_rsa` TEXT, `ps` TEXT, `addtime` TEXT diff --git a/plugins/mysql/index.html b/plugins/mysql/index.html index 78645b3b6..ee348dbdc 100755 --- a/plugins/mysql/index.html +++ b/plugins/mysql/index.html @@ -2,7 +2,7 @@
- +

服务

自启动

配置文件

@@ -49,6 +49,13 @@ #db_tools button:last-child{ margin-right: 0; } + +.conf_p span { + display: inline-block; + margin-right: 10px; + width: 95px; + text-align: right; +} \ No newline at end of file diff --git a/plugins/postgresql/index.py b/plugins/postgresql/index.py new file mode 100755 index 000000000..0a834c1fb --- /dev/null +++ b/plugins/postgresql/index.py @@ -0,0 +1,724 @@ +# coding:utf-8 + +import sys +import io +import os +import time +import subprocess +import re +import json + + +# reload(sys) +# sys.setdefaultencoding('utf-8') + +sys.path.append(os.getcwd() + "/class/core") +import mw + + +if mw.isAppleSystem(): + cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' + info = mw.execShell(cmd) + p = "/usr/local/lib/" + info[0].strip() + "/site-packages" + sys.path.append(p) + + +app_debug = False +if mw.isAppleSystem(): + app_debug = True + + +def getPluginName(): + return 'postgresql' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def getInitDFile(): + if app_debug: + return '/tmp/' + getPluginName() + return '/etc/init.d/' + getPluginName() + + +def getArgs(): + args = sys.argv[2:] + + tmp = {} + args_len = len(args) + + if args_len == 1: + t = args[0].strip('{').strip('}') + t = t.split(':') + tmp[t[0]] = t[1] + elif args_len > 1: + for i in range(len(args)): + t = args[i].split(':') + tmp[t[0]] = t[1] + + return tmp + + +def checkArgs(data, ck=[]): + for i in range(len(ck)): + if not ck[i] in data: + return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!')) + return (True, mw.returnJson(True, 'ok')) + + +def getConf(): + path = getServerDir() + '/data/postgresql.conf' + return path + + +def getDbPort(): + file = getConf() + content = mw.readFile(file) + rep = 'port\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def getSocketFile(): + file = getConf() + content = mw.readFile(file) + rep = 'socket\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def getInitdTpl(version=''): + path = getPluginDir() + '/init.d/postgresql.tpl' + if not os.path.exists(path): + path = getPluginDir() + '/init.d/postgresql.tpl' + return path + + +def contentReplace(content): + service_path = mw.getServerDir() + content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$SERVER_PATH}', service_path) + content = content.replace('{$APP_PATH}', service_path + '/postgresql') + return content + + +def pSqliteDb(dbname='databases'): + file = getServerDir() + '/pgsql.db' + name = 'pgsql' + if not os.path.exists(file): + conn = mw.M(dbname).dbPos(getServerDir(), name) + csql = mw.readFile(getPluginDir() + '/conf/pgsql.sql') + csql_list = csql.split(';') + for index in range(len(csql_list)): + conn.execute(csql_list[index], ()) + else: + # 现有run + # conn = mw.M(dbname).dbPos(getServerDir(), name) + # csql = mw.readFile(getPluginDir() + '/conf/mysql.sql') + # csql_list = csql.split(';') + # for index in range(len(csql_list)): + # conn.execute(csql_list[index], ()) + conn = mw.M(dbname).dbPos(getServerDir(), name) + return conn + + +def pMysqlDb(): + # pymysql + db = mw.getMyORM() + # MySQLdb | + # db = mw.getMyORMDb() + + db.setPort(getDbPort()) + db.setSocket(getSocketFile()) + # db.setCharset("utf8") + db.setPwd(pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')) + return db + + +def initDreplace(version=''): + + conf_dir = getServerDir() + '/etc' + log_dir = getServerDir() + "/logs" + conf_list = [ + conf_dir, + log_dir + ] + for c in conf_list: + if not os.path.exists(c): + os.mkdir(c) + + # my_conf = conf_dir + '/my.cnf' + # if not os.path.exists(my_conf): + # tpl = getPluginDir() + '/conf/my.cnf' + # content = mw.readFile(tpl) + # content = contentReplace(content) + # mw.writeFile(my_conf, content) + + # systemd + system_dir = mw.systemdCfgDir() + service = system_dir + '/postgresql.service' + if os.path.exists(system_dir) and not os.path.exists(service): + tpl = getPluginDir() + '/init.d/postgresql.service.tpl' + service_path = mw.getServerDir() + content = mw.readFile(tpl) + content = contentReplace(content) + mw.writeFile(service, content) + mw.execShell('systemctl daemon-reload') + + if not mw.isAppleSystem(): + mw.execShell('chown -R postgresql:postgresql ' + getServerDir()) + + initd_path = getServerDir() + '/init.d' + if not os.path.exists(initd_path): + os.mkdir(initd_path) + + file_bin = initd_path + '/' + getPluginName() + if not os.path.exists(file_bin): + initd_tpl = getInitdTpl(version) + content = mw.readFile(initd_tpl) + content = contentReplace(content) + mw.writeFile(file_bin, content) + mw.execShell('chmod +x ' + file_bin) + return file_bin + + +def status(version=''): + data = mw.execShell( + "ps -ef|grep postgres |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'") + if data[0] == '': + return 'stop' + return 'start' + + +def getDataDir(): + file = getConf() + content = mw.readFile(file) + rep = 'datadir\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def getPidFile(): + file = getConf() + content = mw.readFile(file) + rep = 'pid-file\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def getErrorLog(): + args = getArgs() + path = getDataDir() + filename = '' + for n in os.listdir(path): + if len(n) < 5: + continue + if n == 'error.log': + filename = path + '/' + n + break + # print filename + if not os.path.exists(filename): + return mw.returnJson(False, '指定文件不存在!') + if 'close' in args: + mw.writeFile(filename, '') + return mw.returnJson(False, '日志已清空') + info = mw.getNumLines(filename, 18) + return mw.returnJson(True, 'OK', info) + + +def getShowLogFile(): + file = getConf() + content = mw.readFile(file) + rep = 'slow-query-log-file\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def pGetDbUser(): + if mw.isAppleSystem(): + user = mw.execShell( + "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() + return user + return 'postgresql' + + +def initPgData(): + serverdir = getServerDir() + if not os.path.exists(serverdir + '/postgresql'): + cmd = 'cd ' + serverdir + ' && ./bin/initdb -D ' + serverdir + "/data" + mw.execShell(cmd) + return False + return True + + +def initPgPwd(): + + serverdir = getServerDir() + pwd = mw.getRandomString(16) + + cmd_pass = "echo \"create user root with superuser password '" + pwd + "'\" | " + cmd_pass = cmd_pass + serverdir + '/bin/psql -d postgres' + data = mw.execShell(cmd_pass) + print(cmd_pass) + print(data) + + pSqliteDb('config').where('id=?', (1,)).save('pg_root', (pwd,)) + return True + + +def myOp(version, method): + # import commands + init_file = initDreplace() + cmd = init_file + ' ' + method + # print(cmd) + try: + isInited = initPgData() + if not isInited: + if mw.isAppleSystem(): + cmd_init_start = init_file + ' start' + subprocess.Popen(cmd_init_start, stdout=subprocess.PIPE, shell=True, + bufsize=4096, stderr=subprocess.PIPE) + + time.sleep(6) + else: + mw.execShell('systemctl start postgresql') + + initPgPwd() + + if mw.isAppleSystem(): + cmd_init_stop = init_file + ' stop' + subprocess.Popen(cmd_init_stop, stdout=subprocess.PIPE, shell=True, + bufsize=4096, stderr=subprocess.PIPE) + time.sleep(3) + else: + mw.execShell('systemctl stop postgresql') + + if mw.isAppleSystem(): + sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, + bufsize=4096, stderr=subprocess.PIPE) + sub.wait(5) + else: + mw.execShell('systemctl ' + method + ' postgresql') + return 'ok' + except Exception as e: + # raise + return method + ":" + str(e) + + +def appCMD(version, action): + return myOp(version, action) + + +def start(version=''): + return appCMD(version, 'start') + + +def stop(version=''): + return appCMD(version, 'stop') + + +def restart(version=''): + return appCMD(version, 'restart') + + +def reload(version=''): + return appCMD(version, 'reload') + + +def initdStatus(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + shell_cmd = 'systemctl status postgresql | grep loaded | grep "enabled;"' + data = mw.execShell(shell_cmd) + if data[0] == '': + return 'fail' + return 'ok' + + +def initdInstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + mw.execShell('systemctl enable postgresql') + return 'ok' + + +def initdUinstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + mw.execShell('systemctl disable postgresql') + return 'ok' + + +def getMyDbPos(): + file = getConf() + content = mw.readFile(file) + rep = 'datadir\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def setMyDbPos(): + args = getArgs() + data = checkArgs(args, ['datadir']) + if not data[0]: + return data[1] + + s_datadir = getMyDbPos() + t_datadir = args['datadir'] + if t_datadir == s_datadir: + return mw.returnJson(False, '与当前存储目录相同,无法迁移文件!') + + if not os.path.exists(t_datadir): + mw.execShell('mkdir -p ' + t_datadir) + + # mw.execShell('/etc/init.d/mysqld stop') + stop() + mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/') + mw.execShell('chown -R mysql mysql ' + t_datadir) + mw.execShell('chmod -R 755 ' + t_datadir) + mw.execShell('rm -f ' + t_datadir + '/*.pid') + mw.execShell('rm -f ' + t_datadir + '/*.err') + + path = getServerDir() + myfile = path + '/etc/my.cnf' + mycnf = mw.readFile(myfile) + mw.writeFile(path + '/etc/my_backup.cnf', mycnf) + + mycnf = mycnf.replace(s_datadir, t_datadir) + mw.writeFile(myfile, mycnf) + start() + + result = mw.execShell( + 'ps aux|grep mysqld| grep -v grep|grep -v python') + if len(result[0]) > 10: + mw.writeFile('data/datadir.pl', t_datadir) + return mw.returnJson(True, '存储目录迁移成功!') + else: + mw.execShell('pkill -9 mysqld') + mw.writeFile(myfile, mw.readFile(path + '/etc/my_backup.cnf')) + start() + return mw.returnJson(False, '文件迁移失败!') + + +def getPgPort(): + file = getConf() + content = mw.readFile(file) + rep = 'port\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def setPgPort(): + args = getArgs() + data = checkArgs(args, ['port']) + if not data[0]: + return data[1] + + port = args['port'] + file = getConf() + content = mw.readFile(file) + rep = "port\s*=\s*([0-9]+)\s*\n" + content = re.sub(rep, 'port = ' + port + '\n', content) + mw.writeFile(file, content) + restart() + return mw.returnJson(True, '编辑成功!') + + +def runInfo(): + + if status(version) == 'stop': + return mw.returnJson(False, 'PG未启动', []) + + db = pMysqlDb() + data = db.query('show global status') + gets = ['Max_used_connections', 'Com_commit', 'Com_rollback', 'Questions', 'Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests', 'Key_reads', 'Key_read_requests', 'Key_writes', + 'Key_write_requests', 'Qcache_hits', 'Qcache_inserts', 'Bytes_received', 'Bytes_sent', 'Aborted_clients', 'Aborted_connects', + 'Created_tmp_disk_tables', 'Created_tmp_tables', 'Innodb_buffer_pool_pages_dirty', 'Opened_files', 'Open_tables', 'Opened_tables', 'Select_full_join', + 'Select_range_check', 'Sort_merge_passes', 'Table_locks_waited', 'Threads_cached', 'Threads_connected', 'Threads_created', 'Threads_running', 'Connections', 'Uptime'] + + result = {} + # print(data) + for d in data: + vname = d["Variable_name"] + for g in gets: + if vname == g: + result[g] = d["Value"] + + # print(result, int(result['Uptime'])) + result['Run'] = int(time.time()) - int(result['Uptime']) + tmp = db.query('show master status') + try: + result['File'] = tmp[0]["File"] + result['Position'] = tmp[0]["Position"] + except: + result['File'] = 'OFF' + result['Position'] = 'OFF' + return mw.getJson(result) + + +def runLog(): + return getServerDir() + "/logs/server.log" + + +def myDbStatus(): + result = {} + db = pMysqlDb() + data = db.query('show variables') + isError = isSqlError(data) + if isError != None: + return isError + + gets = ['table_open_cache', 'thread_cache_size', 'key_buffer_size', 'tmp_table_size', 'max_heap_table_size', 'innodb_buffer_pool_size', + 'innodb_additional_mem_pool_size', 'innodb_log_buffer_size', 'max_connections', 'sort_buffer_size', 'read_buffer_size', 'read_rnd_buffer_size', 'join_buffer_size', 'thread_stack', 'binlog_cache_size'] + result['mem'] = {} + for d in data: + vname = d['Variable_name'] + for g in gets: + # print(g) + if vname == g: + result['mem'][g] = d["Value"] + return mw.getJson(result) + + +def setDbStatus(): + gets = ['key_buffer_size', 'tmp_table_size', 'max_heap_table_size', 'innodb_buffer_pool_size', 'innodb_log_buffer_size', 'max_connections', + 'table_open_cache', 'thread_cache_size', 'sort_buffer_size', 'read_buffer_size', 'read_rnd_buffer_size', 'join_buffer_size', 'thread_stack', 'binlog_cache_size'] + emptys = ['max_connections', 'thread_cache_size', 'table_open_cache'] + args = getArgs() + conFile = getConf() + content = mw.readFile(conFile) + n = 0 + for g in gets: + s = 'M' + if n > 5: + s = 'K' + if g in emptys: + s = '' + rep = '\s*' + g + '\s*=\s*\d+(M|K|k|m|G)?\n' + c = g + ' = ' + args[g] + s + '\n' + if content.find(g) != -1: + content = re.sub(rep, '\n' + c, content, 1) + else: + content = content.replace('[mysqld]\n', '[mysqld]\n' + c) + n += 1 + mw.writeFile(conFile, content) + return mw.returnJson(True, '设置成功!') + + +def __createUser(dbname, username, password, address): + pdb = pMysqlDb() + + if username == 'root': + dbname = '*' + + pdb.execute( + "CREATE USER `%s`@`localhost` IDENTIFIED BY '%s'" % (username, password)) + pdb.execute( + "grant all privileges on %s.* to `%s`@`localhost`" % (dbname, username)) + for a in address.split(','): + pdb.execute( + "CREATE USER `%s`@`%s` IDENTIFIED BY '%s'" % (username, a, password)) + pdb.execute( + "grant all privileges on %s.* to `%s`@`%s`" % (dbname, username, a)) + pdb.execute("flush privileges") + + +def getDbBackupListFunc(dbname=''): + bkDir = mw.getRootDir() + '/backup/database' + blist = os.listdir(bkDir) + r = [] + + bname = 'db_' + dbname + blen = len(bname) + for x in blist: + fbstr = x[0:blen] + if fbstr == bname: + r.append(x) + return r + + +def setDbBackup(): + args = getArgs() + data = checkArgs(args, ['name']) + if not data[0]: + return data[1] + + scDir = getPluginDir() + '/scripts/backup.py' + cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3' + os.system(cmd) + return mw.returnJson(True, 'ok') + + +def importDbBackup(): + args = getArgs() + data = checkArgs(args, ['file', 'name']) + if not data[0]: + return data[1] + + file = args['file'] + name = args['name'] + + file_path = mw.getRootDir() + '/backup/database/' + file + file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '') + + if not os.path.exists(file_path_sql): + cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file + mw.execShell(cmd) + + pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') + + mysql_cmd = mw.getRootDir() + '/server/mysql/bin/mysql -uroot -p' + pwd + \ + ' ' + name + ' < ' + file_path_sql + + # print(mysql_cmd) + os.system(mysql_cmd) + return mw.returnJson(True, 'ok') + + +def deleteDbBackup(): + args = getArgs() + data = checkArgs(args, ['filename']) + if not data[0]: + return data[1] + + bkDir = mw.getRootDir() + '/backup/database' + + os.remove(bkDir + '/' + args['filename']) + return mw.returnJson(True, 'ok') + + +def getDbBackupList(): + args = getArgs() + data = checkArgs(args, ['name']) + if not data[0]: + return data[1] + + r = getDbBackupListFunc(args['name']) + bkDir = mw.getRootDir() + '/backup/database' + rr = [] + for x in range(0, len(r)): + p = bkDir + '/' + r[x] + data = {} + data['name'] = r[x] + + rsize = os.path.getsize(p) + data['size'] = mw.toSize(rsize) + + t = os.path.getctime(p) + t = time.localtime(t) + + data['time'] = time.strftime('%Y-%m-%d %H:%M:%S', t) + rr.append(data) + + data['file'] = p + + return mw.returnJson(True, 'ok', rr) + + +def getDbList(): + 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 = '' + if not search == '': + condition = "name like '%" + search + "%'" + field = 'id,pid,name,username,password,accept,rw,ps,addtime' + clist = conn.where(condition, ()).field( + field).limit(limit).order('id desc').select() + + for x in range(0, len(clist)): + dbname = clist[x]['name'] + blist = getDbBackupListFunc(dbname) + # print(blist) + clist[x]['is_backup'] = False + if len(blist) > 0: + clist[x]['is_backup'] = True + + count = conn.where(condition, ()).count() + _page = {} + _page['count'] = count + _page['p'] = page + _page['row'] = page_size + _page['tojs'] = 'dbList' + data['page'] = mw.getPage(_page) + data['data'] = clist + + info = {} + info['root_pwd'] = pSqliteDb('config').where( + 'id=?', (1,)).getField('mysql_root') + data['info'] = info + + return mw.getJson(data) + + +def installPreInspection(version): + return 'ok' + + +def uninstallPreInspection(version): + # return "请手动删除MySQL[{}]".format(version) + return 'ok' + +if __name__ == "__main__": + func = sys.argv[1] + + version = "14.4" + version_pl = getServerDir() + "/version.pl" + if os.path.exists(version_pl): + version = mw.readFile(version_pl).strip() + + if func == 'status': + print(status(version)) + elif func == 'start': + print(start(version)) + elif func == 'stop': + print(stop(version)) + elif func == 'restart': + print(restart(version)) + elif func == 'reload': + print(reload(version)) + elif func == 'initd_status': + print(initdStatus()) + elif func == 'initd_install': + print(initdInstall()) + elif func == 'initd_uninstall': + print(initdUinstall()) + elif func == 'install_pre_inspection': + print(installPreInspection(version)) + elif func == 'uninstall_pre_inspection': + print(uninstallPreInspection(version)) + elif func == 'conf': + print(getConf()) + elif func == 'run_info': + print(runInfo()) + elif func == 'run_log': + print(runLog()) + elif func == 'pg_port': + print(getPgPort()) + elif func == 'set_pg_port': + print(setPgPort()) + elif func == 'get_db_list': + print(getDbList()) + else: + print('error') diff --git a/plugins/postgresql/info.json b/plugins/postgresql/info.json new file mode 100755 index 000000000..342f924d8 --- /dev/null +++ b/plugins/postgresql/info.json @@ -0,0 +1,18 @@ +{ + "title":"PostgreSQL", + "tip":"soft", + "name":"postgresql", + "type":"运行环境", + "ps":"[DEV]功能强大的开源数据库", + "coexist": false, + "install_pre_inspection":true, + "uninstall_pre_inspection":true, + "versions":["14.4"], + "shell":"install.sh", + "checks":"server/postgresql", + "path":"server/postgresql", + "author":"postgresql", + "home":"https://www.postgresql.org/", + "date":"2022-08-07", + "pid": "2" +} \ No newline at end of file diff --git a/plugins/postgresql/init.d/postgresql.service.tpl b/plugins/postgresql/init.d/postgresql.service.tpl new file mode 100644 index 000000000..fd856395a --- /dev/null +++ b/plugins/postgresql/init.d/postgresql.service.tpl @@ -0,0 +1,16 @@ +[Unit] +Description=PostgreSQL: a powerful open source database +After=network.target + +[Service] +Type=forking +User=postgres +Group=postgres +WorkingDirectory={$APP_PATH} +ExecStart={$APP_PATH}/pg_ctl start -D {$APP_PATH}/data +ExecReload={$APP_PATH}/pg_ctl restart -D {$APP_PATH}/data +ExecStop={$APP_PATH}/pg_ctl stop -D {$APP_PATH}/data +PrivateTmp=false + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/plugins/postgresql/init.d/postgresql.tpl b/plugins/postgresql/init.d/postgresql.tpl new file mode 100644 index 000000000..a0c5cf3db --- /dev/null +++ b/plugins/postgresql/init.d/postgresql.tpl @@ -0,0 +1,66 @@ +#!/bin/bash +# chkconfig: 2345 55 25 +# description: PostgreSQL Service + +### BEGIN INIT INFO +# Provides: Midoks +# Required-Start: $all +# Required-Stop: $all +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: starts PostgreSQL +# Description: starts the PostgreSQL +### END INIT INFO + + +PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export LC_ALL="en_US.UTF-8" + +MW_PATH={$SERVER_PATH} +PATH=$PATH:$MW_PATH/bin + +if [ -f $MW_PATH/bin/activate ];then + source $MW_PATH/bin/activate +fi + +pg_start() +{ + touch {$APP_PATH}/logs/server.log + {$APP_PATH}/bin/pg_ctl -D {$APP_PATH}/data -l {$APP_PATH}/logs/server.log start +} + + +pg_stop() +{ + {$APP_PATH}/bin/pg_ctl -D {$APP_PATH}/data -l {$APP_PATH}/logs/server.log stop +} + + + +pg_status() +{ + isStart=$(ps aux |grep 'postgres'|grep -v grep|awk '{print $2}') + if [ "$isStart" != '' ];then + echo -e "\033[32mPostgreSQL (pid $isStart) already running\033[0m" + else + echo -e "\033[31mPostgreSQL not running\033[0m" + fi +} + + +pg_reload() +{ + pg_stop + pg_start +} + + + +case "$1" in + 'start') pg_start;; + 'stop') pg_stop;; + 'reload') pg_reload;; + 'restart') + pg_stop + pg_start;; +esac diff --git a/plugins/postgresql/install.sh b/plugins/postgresql/install.sh new file mode 100755 index 000000000..741fc3540 --- /dev/null +++ b/plugins/postgresql/install.sh @@ -0,0 +1,42 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + + +install_tmp=${rootPath}/tmp/mw_install.pl + +action=$1 +type=$2 + +if [ "${2}" == "" ];then + echo '缺少安装脚本...' > $install_tmp + exit 0 +fi + +if [ ! -d $curPath/versions/$2 ];then + echo '缺少安装脚本2...' > $install_tmp + exit 0 +fi + +# if [ "${action}" == "uninstall" ];then +# if [ -f /usr/lib/systemd/system/postgresql.service ] || [ -f /lib/systemd/system/postgresql.service ];then +# systemctl stop postgresql +# systemctl disable postgresql +# rm -rf /usr/lib/systemd/system/postgresql.service +# rm -rf /lib/systemd/system/postgresql.service +# systemctl daemon-reload +# fi +# fi + +sh -x $curPath/versions/$2/install.sh $1 + +# if [ "${action}" == "install" ] && [ -d $serverPath/postgresql ];then +# #初始化 +# cd ${rootPath} && python3 ${rootPath}/plugins/postgresql/index.py start ${type} +# cd ${rootPath} && python3 ${rootPath}/plugins/postgresql/index.py initd_install ${type} +# fi diff --git a/plugins/postgresql/js/postgresql.js b/plugins/postgresql/js/postgresql.js new file mode 100755 index 000000000..ad71f3468 --- /dev/null +++ b/plugins/postgresql/js/postgresql.js @@ -0,0 +1,1969 @@ +function str2Obj(str){ + var data = {}; + kv = str.split('&'); + for(i in kv){ + v = kv[i].split('='); + data[v[0]] = v[1]; + } + return data; +} + +function myPost(method,args,callback, title){ + + var _args = null; + if (typeof(args) == 'string'){ + _args = JSON.stringify(str2Obj(args)); + } else { + _args = JSON.stringify(args); + } + + var _title = '正在获取...'; + if (typeof(title) != 'undefined'){ + _title = title; + } + + var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 }); + $.post('/plugins/run', {name:'postgresql', func:method, args:_args}, function(data) { + layer.close(loadT); + if (!data.status){ + layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function myPostN(method,args,callback, title){ + + var _args = null; + if (typeof(args) == 'string'){ + _args = JSON.stringify(str2Obj(args)); + } else { + _args = JSON.stringify(args); + } + + var _title = '正在获取...'; + if (typeof(title) != 'undefined'){ + _title = title; + } + $.post('/plugins/run', {name:'postgresql', func:method, args:_args}, function(data) { + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function myAsyncPost(method,args){ + var _args = null; + if (typeof(args) == 'string'){ + _args = JSON.stringify(str2Obj(args)); + } else { + _args = JSON.stringify(args); + } + + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + return syncPost('/plugins/run', {name:'mysql', func:method, args:_args}); +} + +function runInfo(){ + myPost('run_info','',function(data){ + + var rdata = $.parseJSON(data.data); + if (typeof(rdata['status']) != 'undefined'){ + layer.msg(rdata['msg'],{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + var cache_size = ((parseInt(rdata.Qcache_hits) / (parseInt(rdata.Qcache_hits) + parseInt(rdata.Qcache_inserts))) * 100).toFixed(2) + '%'; + if (cache_size == 'NaN%') cache_size = 'OFF'; + var Con = '
\ + \ + \ + \ + \ + \ + \ +
启动时间' + getLocalTime(rdata.Run) + '每秒查询' + parseInt(rdata.Questions / rdata.Uptime) + '
总连接次数' + rdata.Connections + '每秒事务' + parseInt((parseInt(rdata.Com_commit) + parseInt(rdata.Com_rollback)) / rdata.Uptime) + '
发送' + toSize(rdata.Bytes_sent) + 'File' + rdata.File + '
接收' + toSize(rdata.Bytes_received) + 'Position' + rdata.Position + '
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
活动/峰值连接数' + rdata.Threads_running + '/' + rdata.Max_used_connections + '若值过大,增加max_connections
线程缓存命中率' + ((1 - rdata.Threads_created / rdata.Connections) * 100).toFixed(2) + '%若过低,增加thread_cache_size
索引命中率' + ((1 - rdata.Key_reads / rdata.Key_read_requests) * 100).toFixed(2) + '%若过低,增加key_buffer_size
Innodb索引命中率' + ((1 - rdata.Innodb_buffer_pool_reads / rdata.Innodb_buffer_pool_read_requests) * 100).toFixed(2) + '%若过低,增加innodb_buffer_pool_size
查询缓存命中率' + cache_size + '' + lan.soft.mysql_status_ps5 + '
创建临时表到磁盘' + ((rdata.Created_tmp_disk_tables / rdata.Created_tmp_tables) * 100).toFixed(2) + '%若过大,尝试增加tmp_table_size
已打开的表' + rdata.Open_tables + '若过大,增加table_cache_size
没有使用索引的量' + rdata.Select_full_join + '若不为0,请检查数据表的索引是否合理
没有索引的JOIN量' + rdata.Select_range_check + '若不为0,请检查数据表的索引是否合理
排序后的合并次数' + rdata.Sort_merge_passes + '若值过大,增加sort_buffer_size
锁表次数' + rdata.Table_locks_waited + '若值过大,请考虑增加您的数据库性能
'; + $(".soft-man-con").html(Con); + }); +} + + +function myDbPos(){ + myPost('my_db_pos','',function(data){ + var con = '
\ +
\ + \ + \ + \ +
'; + $(".soft-man-con").html(con); + + $('#btn_change_path').click(function(){ + var datadir = $("input[name='datadir']").val(); + myPost('set_db_pos','datadir='+datadir,function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg,{icon:rdata.status ? 1 : 5,time:2000,shade: [0.3, '#000']}); + }); + }); + }); +} + +function pgPort(){ + myPost('pg_port','',function(data){ + var con = '
\ +
\ + \ + \ +
'; + $(".soft-man-con").html(con); + + $('#btn_change_port').click(function(){ + var port = $("input[name='port']").val(); + myPost('set_pg_port','port='+port,function(data){ + var rdata = $.parseJSON(data.data); + if (rdata.status){ + layer.msg('修改成功!',{icon:1,time:2000,shade: [0.3, '#000']}); + } else { + layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']}); + } + }); + }); + }); +} + + +//数据库存储信置 +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 = '

\ + \ + \ +

'; + $(".soft-man-con").html(LimitCon); + }); +} + + + + +//数据库配置状态 +function myPerfOpt() { + //获取MySQL配置 + myPost('db_status','',function(data){ + var rdata = $.parseJSON(data.data); + // console.log(rdata); + var key_buffer_size = toSizeM(rdata.mem.key_buffer_size); + var query_cache_size = toSizeM(rdata.mem.query_cache_size); + var tmp_table_size = toSizeM(rdata.mem.tmp_table_size); + var innodb_buffer_pool_size = toSizeM(rdata.mem.innodb_buffer_pool_size); + var innodb_additional_mem_pool_size = toSizeM(rdata.mem.innodb_additional_mem_pool_size); + var innodb_log_buffer_size = toSizeM(rdata.mem.innodb_log_buffer_size); + + var sort_buffer_size = toSizeM(rdata.mem.sort_buffer_size); + var read_buffer_size = toSizeM(rdata.mem.read_buffer_size); + var read_rnd_buffer_size = toSizeM(rdata.mem.read_rnd_buffer_size); + var join_buffer_size = toSizeM(rdata.mem.join_buffer_size); + var thread_stack = toSizeM(rdata.mem.thread_stack); + var binlog_cache_size = toSizeM(rdata.mem.binlog_cache_size); + + var a = key_buffer_size + query_cache_size + tmp_table_size + innodb_buffer_pool_size + innodb_additional_mem_pool_size + innodb_log_buffer_size; + var b = sort_buffer_size + read_buffer_size + read_rnd_buffer_size + join_buffer_size + thread_stack + binlog_cache_size; + var memSize = a + rdata.mem.max_connections * b; + + + var memCon = '
\ +
最大使用内存: \ + \ + ' + lan.soft.mysql_set_maxmem + ': MB\ +
\ +

key_buffer_sizeMB, ' + lan.soft.mysql_set_key_buffer_size + '

\ +

query_cache_sizeMB, ' + lan.soft.mysql_set_query_cache_size + '

\ +

tmp_table_sizeMB, ' + lan.soft.mysql_set_tmp_table_size + '

\ +

innodb_buffer_pool_sizeMB, ' + lan.soft.mysql_set_innodb_buffer_pool_size + '

\ +

innodb_log_buffer_sizeMB, ' + lan.soft.mysql_set_innodb_log_buffer_size + '

\ +

innodb_additional_mem_pool_sizeMB

\ +

sort_buffer_sizeKB * ' + lan.soft.mysql_set_conn + ', ' + lan.soft.mysql_set_sort_buffer_size + '

\ +

read_buffer_sizeKB * ' + lan.soft.mysql_set_conn + ', ' + lan.soft.mysql_set_read_buffer_size + '

\ +

read_rnd_buffer_sizeKB * ' + lan.soft.mysql_set_conn + ', ' + lan.soft.mysql_set_read_rnd_buffer_size + '

\ +

join_buffer_sizeKB * ' + lan.soft.mysql_set_conn + ', ' + lan.soft.mysql_set_join_buffer_size + '

\ +

thread_stackKB * ' + lan.soft.mysql_set_conn + ', ' + lan.soft.mysql_set_thread_stack + '

\ +

binlog_cache_sizeKB * ' + lan.soft.mysql_set_conn + ', ' + lan.soft.mysql_set_binlog_cache_size + '

\ +

thread_cache_size ' + lan.soft.mysql_set_thread_cache_size + '

\ +

table_open_cache ' + lan.soft.mysql_set_table_open_cache + '

\ +

max_connections ' + lan.soft.mysql_set_max_connections + '

\ +
\ +
' + + $(".soft-man-con").html(memCon); + + $(".conf_p input[name*='size'],.conf_p input[name='max_connections'],.conf_p input[name='thread_stack']").change(function() { + comMySqlMem(); + }); + + $(".conf_p select[name='mysql_set']").change(function() { + mySQLMemOpt($(this).val()); + comMySqlMem(); + }); + }); +} + +function reBootMySqld(){ + pluginOpService('mysql','restart',''); +} + + +//设置MySQL配置参数 +function setMySQLConf() { + $.post('/system/system_total', '', function(memInfo) { + var memSize = memInfo['memTotal']; + var setSize = parseInt($("input[name='memSize']").val()); + + if(memSize < setSize){ + var errMsg = "错误,内存分配过高!

物理内存: {1}MB
最大使用内存: {2}MB
可能造成的后果: 导致数据库不稳定,甚至无法启动MySQLd服务!"; + var msg = errMsg.replace('{1}',memSize).replace('{2}',setSize); + layer.msg(msg,{icon:2,time:5000}); + return; + } + + var query_cache_size = parseInt($("input[name='query_cache_size']").val()); + var query_cache_type = 0; + if (query_cache_size > 0) { + query_cache_type = 1; + } + var data = { + key_buffer_size: parseInt($("input[name='key_buffer_size']").val()), + query_cache_size: query_cache_size, + query_cache_type: query_cache_type, + tmp_table_size: parseInt($("input[name='tmp_table_size']").val()), + max_heap_table_size: parseInt($("input[name='tmp_table_size']").val()), + innodb_buffer_pool_size: parseInt($("input[name='innodb_buffer_pool_size']").val()), + innodb_log_buffer_size: parseInt($("input[name='innodb_log_buffer_size']").val()), + sort_buffer_size: parseInt($("input[name='sort_buffer_size']").val()), + read_buffer_size: parseInt($("input[name='read_buffer_size']").val()), + read_rnd_buffer_size: parseInt($("input[name='read_rnd_buffer_size']").val()), + join_buffer_size: parseInt($("input[name='join_buffer_size']").val()), + thread_stack: parseInt($("input[name='thread_stack']").val()), + binlog_cache_size: parseInt($("input[name='binlog_cache_size']").val()), + thread_cache_size: parseInt($("input[name='thread_cache_size']").val()), + table_open_cache: parseInt($("input[name='table_open_cache']").val()), + max_connections: parseInt($("input[name='max_connections']").val()) + }; + + myPost('set_db_status', data, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + reBootMySqld(); + },{ icon: rdata.status ? 1 : 2 }); + }); + },'json'); +} + + +//MySQL内存优化方案 +function mySQLMemOpt(opt) { + var query_size = parseInt($("input[name='query_cache_size']").val()); + switch (opt) { + case '0': + $("input[name='key_buffer_size']").val(8); + if (query_size) $("input[name='query_cache_size']").val(4); + $("input[name='tmp_table_size']").val(8); + $("input[name='innodb_buffer_pool_size']").val(16); + $("input[name='sort_buffer_size']").val(256); + $("input[name='read_buffer_size']").val(256); + $("input[name='read_rnd_buffer_size']").val(128); + $("input[name='join_buffer_size']").val(128); + $("input[name='thread_stack']").val(256); + $("input[name='binlog_cache_size']").val(32); + $("input[name='thread_cache_size']").val(4); + $("input[name='table_open_cache']").val(32); + $("input[name='max_connections']").val(500); + break; + case '1': + $("input[name='key_buffer_size']").val(128); + if (query_size) $("input[name='query_cache_size']").val(64); + $("input[name='tmp_table_size']").val(64); + $("input[name='innodb_buffer_pool_size']").val(256); + $("input[name='sort_buffer_size']").val(768); + $("input[name='read_buffer_size']").val(768); + $("input[name='read_rnd_buffer_size']").val(512); + $("input[name='join_buffer_size']").val(1024); + $("input[name='thread_stack']").val(256); + $("input[name='binlog_cache_size']").val(64); + $("input[name='thread_cache_size']").val(64); + $("input[name='table_open_cache']").val(128); + $("input[name='max_connections']").val(100); + break; + case '2': + $("input[name='key_buffer_size']").val(256); + if (query_size) $("input[name='query_cache_size']").val(128); + $("input[name='tmp_table_size']").val(384); + $("input[name='innodb_buffer_pool_size']").val(384); + $("input[name='sort_buffer_size']").val(768); + $("input[name='read_buffer_size']").val(768); + $("input[name='read_rnd_buffer_size']").val(512); + $("input[name='join_buffer_size']").val(2048); + $("input[name='thread_stack']").val(256); + $("input[name='binlog_cache_size']").val(64); + $("input[name='thread_cache_size']").val(96); + $("input[name='table_open_cache']").val(192); + $("input[name='max_connections']").val(200); + break; + case '3': + $("input[name='key_buffer_size']").val(384); + if (query_size) $("input[name='query_cache_size']").val(192); + $("input[name='tmp_table_size']").val(512); + $("input[name='innodb_buffer_pool_size']").val(512); + $("input[name='sort_buffer_size']").val(1024); + $("input[name='read_buffer_size']").val(1024); + $("input[name='read_rnd_buffer_size']").val(768); + $("input[name='join_buffer_size']").val(2048); + $("input[name='thread_stack']").val(256); + $("input[name='binlog_cache_size']").val(128); + $("input[name='thread_cache_size']").val(128); + $("input[name='table_open_cache']").val(384); + $("input[name='max_connections']").val(300); + break; + case '4': + $("input[name='key_buffer_size']").val(512); + if (query_size) $("input[name='query_cache_size']").val(256); + $("input[name='tmp_table_size']").val(1024); + $("input[name='innodb_buffer_pool_size']").val(1024); + $("input[name='sort_buffer_size']").val(2048); + $("input[name='read_buffer_size']").val(2048); + $("input[name='read_rnd_buffer_size']").val(1024); + $("input[name='join_buffer_size']").val(4096); + $("input[name='thread_stack']").val(384); + $("input[name='binlog_cache_size']").val(192); + $("input[name='thread_cache_size']").val(192); + $("input[name='table_open_cache']").val(1024); + $("input[name='max_connections']").val(400); + break; + case '5': + $("input[name='key_buffer_size']").val(1024); + if (query_size) $("input[name='query_cache_size']").val(384); + $("input[name='tmp_table_size']").val(2048); + $("input[name='innodb_buffer_pool_size']").val(4096); + $("input[name='sort_buffer_size']").val(4096); + $("input[name='read_buffer_size']").val(4096); + $("input[name='read_rnd_buffer_size']").val(2048); + $("input[name='join_buffer_size']").val(8192); + $("input[name='thread_stack']").val(512); + $("input[name='binlog_cache_size']").val(256); + $("input[name='thread_cache_size']").val(256); + $("input[name='table_open_cache']").val(2048); + $("input[name='max_connections']").val(500); + break; + } +} + +//计算MySQL内存开销 +function comMySqlMem() { + var key_buffer_size = parseInt($("input[name='key_buffer_size']").val()); + var query_cache_size = parseInt($("input[name='query_cache_size']").val()); + var tmp_table_size = parseInt($("input[name='tmp_table_size']").val()); + var innodb_buffer_pool_size = parseInt($("input[name='innodb_buffer_pool_size']").val()); + var innodb_additional_mem_pool_size = parseInt($("input[name='innodb_additional_mem_pool_size']").val()); + var innodb_log_buffer_size = parseInt($("input[name='innodb_log_buffer_size']").val()); + + var sort_buffer_size = $("input[name='sort_buffer_size']").val() / 1024; + var read_buffer_size = $("input[name='read_buffer_size']").val() / 1024; + var read_rnd_buffer_size = $("input[name='read_rnd_buffer_size']").val() / 1024; + var join_buffer_size = $("input[name='join_buffer_size']").val() / 1024; + var thread_stack = $("input[name='thread_stack']").val() / 1024; + var binlog_cache_size = $("input[name='binlog_cache_size']").val() / 1024; + var max_connections = $("input[name='max_connections']").val(); + + var a = key_buffer_size + query_cache_size + tmp_table_size + innodb_buffer_pool_size + innodb_additional_mem_pool_size + innodb_log_buffer_size + var b = sort_buffer_size + read_buffer_size + read_rnd_buffer_size + join_buffer_size + thread_stack + binlog_cache_size + var memSize = a + max_connections * b + $("input[name='memSize']").val(memSize.toFixed(2)); +} + +function syncGetDatabase(){ + myPost('sync_get_databases', null, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + dbList(); + },{ icon: rdata.status ? 1 : 2 }); + }); +} + +function syncToDatabase(type){ + var data = []; + $('input[type="checkbox"].check:checked').each(function () { + if (!isNaN($(this).val())) data.push($(this).val()); + }); + var postData = 'type='+type+'&ids='+JSON.stringify(data); + myPost('sync_to_databases', postData, function(data){ + var rdata = $.parseJSON(data.data); + // console.log(rdata); + showMsg(rdata.msg,function(){ + dbList(); + },{ icon: rdata.status ? 1 : 2 }); + }); +} + +function setRootPwd(type, pwd){ + if (type==1){ + var data = $("#mod_pwd").serialize(); + myPost('set_root_pwd', data, function(data){ + var rdata = $.parseJSON(data.data); + // console.log(rdata); + showMsg(rdata.msg,function(){ + dbList(); + $('.layui-layer-close1').click(); + },{icon: rdata.status ? 1 : 2}); + }); + return; + } + + var index = layer.open({ + type: 1, + skin: 'demo-class', + area: '500px', + title: '修改数据库密码', + closeBtn: 1, + shift: 5, + shadeClose: true, + content: "

\ +
\ + root密码\ +
\ +
\ +
\ + \ + \ +
\ +
", + }); + + $('#my_mod_close').click(function(){ + $('.layui-layer-close1').click(); + }); +} + +function showHidePass(obj){ + var a = "glyphicon-eye-open"; + var b = "glyphicon-eye-close"; + + if($(obj).hasClass(a)){ + $(obj).removeClass(a).addClass(b); + $(obj).prev().text($(obj).prev().attr('data-pw')) + } + else{ + $(obj).removeClass(b).addClass(a); + $(obj).prev().text('***'); + } +} + +function copyPass(password){ + var clipboard = new ClipboardJS('#bt_copys'); + clipboard.on('success', function (e) { + layer.msg('复制成功',{icon:1,time:2000}); + }); + + clipboard.on('error', function (e) { + layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000}); + }); + $("#bt_copys").attr('data-clipboard-text',password); + $("#bt_copys").click(); +} + +function checkSelect(){ + setTimeout(function () { + var num = $('input[type="checkbox"].check:checked').length; + // console.log(num); + if (num == 1) { + $('button[batch="true"]').hide(); + $('button[batch="false"]').show(); + }else if (num>1){ + $('button[batch="true"]').show(); + $('button[batch="false"]').show(); + }else{ + $('button[batch="true"]').hide(); + $('button[batch="false"]').hide(); + } + },5) +} + +function setDbRw(id,username,val){ + myPost('get_db_rw',{id:id,username:username,rw:val}, function(data){ + var rdata = $.parseJSON(data.data); + // layer.msg(rdata.msg,{icon:rdata.status ? 1 : 5,shade: [0.3, '#000']}); + showMsg(rdata.msg, function(){ + dbList(); + },{icon:rdata.status ? 1 : 5,shade: [0.3, '#000']}, 2000); + + }); +} + +function setDbAccess(username){ + myPost('get_db_access','username='+username, function(data){ + var rdata = $.parseJSON(data.data); + if (!rdata.status){ + layer.msg(rdata.msg,{icon:2,shade: [0.3, '#000']}); + return; + } + + var index = layer.open({ + type: 1, + area: '500px', + title: '设置数据库权限', + closeBtn: 1, + shift: 5, + btn:["提交","取消"], + shadeClose: true, + content: "
\ +
\ + 访问权限\ +
\ + \ +
\ +
\ +
", + success:function(){ + if (rdata.msg == '127.0.0.1'){ + $('select[name="dataAccess"]').find("option[value='127.0.0.1']").attr("selected",true); + } else if (rdata.msg == '%'){ + $('select[name="dataAccess"]').find('option[value="%"]').attr("selected",true); + } else if ( rdata.msg == 'ip' ){ + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } else { + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } + + $('select[name="dataAccess"]').change(function(){ + var v = $(this).val(); + if (v == 'ip'){ + $(this).after(""); + } else { + $('#dataAccess_subid').remove(); + } + }); + }, + yes:function(index){ + var data = $("#set_db_access").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['access']){ + dataObj['access'] = dataObj['dataAccess']; + if ( dataObj['dataAccess'] == 'ip'){ + if (dataObj['address']==''){ + layer.msg('IP地址不能空!',{icon:2,shade: [0.3, '#000']}); + return; + } + dataObj['access'] = dataObj['address']; + } + } + dataObj['username'] = username; + myPost('set_db_access', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + dbList(); + },{icon: rdata.status ? 1 : 2}); + }); + } + }); + + }); +} + +function setDbPass(id, username, password){ + + var index = layer.open({ + type: 1, + area: '500px', + title: '修改数据库密码', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["提交","关闭"], + content: "
\ +
\ + 用户名\ +
\ +
\ +
\ + 密码\ +
\ +
\ + \ +
", + yes:function(index){ + var data = $("#mod_pwd").serialize(); + myPost('set_user_pwd', data, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + dbList(); + },{icon: rdata.status ? 1 : 2}); + }); + } + }); +} + +function addDatabase(type){ + if (type==1){ + var data = $("#add_db").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['address']){ + dataObj['address'] = dataObj['dataAccess']; + } + myPost('add_db', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + if (rdata.status){ + dbList(); + } + $('.layui-layer-close1').click(); + },{icon: rdata.status ? 1 : 2},600); + }); + return; + } + 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(); + } + }); +} + +function delDb(id, name){ + safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){ + var data='id='+id+'&name='+name + myPost('del_db', data, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + dbList(); + $('.layui-layer-close1').click(); + },{icon: rdata.status ? 1 : 2}, 600); + }); + }); +} + +function delDbBatch(){ + var arr = []; + $('input[type="checkbox"].check:checked').each(function () { + var _val = $(this).val(); + var _name = $(this).parent().next().text(); + if (!isNaN(_val)) { + arr.push({'id':_val,'name':_name}); + } + }); + + safeMessage('批量删除数据库','您共选择了[2]个数据库,删除后将无法恢复,真的要删除吗?',function(){ + var i = 0; + $(arr).each(function(){ + var data = myAsyncPost('del_db', this); + var rdata = $.parseJSON(data.data); + if (!rdata.status){ + layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']}); + } + i++; + }); + + var msg = '成功删除['+i+']个数据库!'; + showMsg(msg,function(){ + dbList(); + },{icon: 1}, 600); + }); +} + + +function setDbPs(id, name, obj) { + var _span = $(obj); + var _input = $(""); + _span.hide().after(_input); + _input.focus(); + _input.blur(function(){ + $(this).remove(); + var ps = _input.val(); + _span.text(ps).show(); + var data = {name:name,id:id,ps:ps}; + myPost('set_db_ps', data, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); + }); + }); + _input.keyup(function(){ + if(event.keyCode == 13){ + _input.trigger('blur'); + } + }); +} + +function openPhpmyadmin(name,username,password){ + + data = syncPost('/plugins/check',{'name':'phpmyadmin'}); + + + if (!data.status){ + layer.msg(data.msg,{icon:2,shade: [0.3, '#000']}); + return; + } + + data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'status'}); + if (data.data != 'start'){ + layer.msg('phpMyAdmin未启动',{icon:2,shade: [0.3, '#000']}); + return; + } + // console.log(data); + data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_home_page'}); + var rdata = $.parseJSON(data.data); + if (!rdata.status){ + layer.msg(rdata.msg,{icon:2,shade: [0.3, '#000']}); + return; + } + $("#toPHPMyAdmin").attr('action',rdata.data); + + if($("#toPHPMyAdmin").attr('action').indexOf('phpmyadmin') == -1){ + layer.msg('请先安装phpMyAdmin',{icon:2,shade: [0.3, '#000']}); + setTimeout(function(){ window.location.href = '/soft'; },3000); + return; + } + + //检查版本 + data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'version'}); + bigVer = data.data.split('.')[0] + if (bigVer>=4.5){ + + setTimeout(function(){ + $("#toPHPMyAdmin").submit(); + },3000); + layer.msg('phpMyAdmin['+data.data+']需要手动登录😭',{icon:16,shade: [0.3, '#000'],time:4000}); + + } else{ + var murl = $("#toPHPMyAdmin").attr('action'); + $("#pma_username").val(username); + $("#pma_password").val(password); + $("#db").val(name); + + layer.msg('正在打开phpMyAdmin',{icon:16,shade: [0.3, '#000'],time:2000}); + + setTimeout(function(){ + $("#toPHPMyAdmin").submit(); + },3000); + } +} + +function delBackup(filename,name){ + myPost('delete_db_backup',{filename:filename},function(){ + layer.msg('执行成功!'); + setTimeout(function(){ + $('.layui-layer-close2').click(); + setBackup(name); + },2000); + }); +} + +function downloadBackup(file){ + window.open('/files/download?filename='+encodeURIComponent(file)); +} + +function importBackup(file,name){ + myPost('import_db_backup',{file:file,name:name}, function(data){ + // console.log(data); + layer.msg('执行成功!'); + }); +} + +function setBackup(db_name,obj){ + myPost('get_db_backup_list', {name:db_name}, function(data){ + + var rdata = $.parseJSON(data.data); + var tbody = ''; + for (var i = 0; i < rdata.data.length; i++) { + tbody += '\ + ' + rdata.data[i]['name'] + '\ + ' + rdata.data[i]['size'] + '\ + ' + rdata.data[i]['time'] + '\ + \ + 导入 | \ + 下载 | \ + 删除\ + \ + '; + } + + var s = layer.open({ + type: 1, + title: "数据库备份详情", + area: ['600px', '280px'], + closeBtn: 2, + shadeClose: false, + content: '
\ +
\ + \ +
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + ' + tbody + '\ +
文件名称文件大小备份时间操作
\ +
\ +
\ +
' + }); + + $('#btn_backup').click(function(){ + myPost('set_db_backup',{name:db_name}, function(data){ + layer.msg('执行成功!'); + + setTimeout(function(){ + layer.close(s); + setBackup(db_name,obj); + },2000); + }); + }); + }); +} + + +function dbList(page, search){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + if(typeof(search) != 'undefined'){ + _data['search'] = search; + } + myPost('get_db_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + var list = ''; + for(i in rdata.data){ + list += ''; + list +=''; + list += '' + rdata.data[i]['name'] +''; + list += '' + rdata.data[i]['username'] +''; + list += '' + + '***' + + ''+ + ''+ + ''; + + + list += ''+rdata.data[i]['ps']+''; + list += ''; + + list += ''+(rdata.data[i]['is_backup']?'备份':'未备份') +' | '; + + var rw = ''; + var rw_change = 'all'; + if (typeof(rdata.data[i]['rw'])!='undefined'){ + var rw_val = '读写'; + if (rdata.data[i]['rw'] == 'all'){ + rw_val = "所有"; + rw_change = 'rw'; + } else if (rdata.data[i]['rw'] == 'rw'){ + rw_val = "读写"; + rw_change = 'r'; + } else if (rdata.data[i]['rw'] == 'r'){ + rw_val = "只读"; + rw_change = 'all'; + } + rw = ''+rw_val+' | '; + } + + + list += '工具 | ' + + '权限 | ' + + rw + + '改密 | ' + + '删除' + + ''; + list += ''; + } + + // + var con = '
\ + \ + \ + \ + \ + \ + \ +
\ +
\ + \ + \ + \ + \ + \ + '+ + // ''+ + '\ + \ + \ + '+ list +'\ +
数据库名用户名密码备份备注操作
\ +
\ +
\ +
\ + 同步选中\ + 同步所有\ + 从服务器获取\ +
\ +
\ +
'; + + con += ''; + + $(".soft-man-con").html(con); + $('#databasePage').html(rdata.page); + + readerTableChecked(); + }); +} + +function repCheckeds(tables) { + var dbs = [] + if (tables) { + dbs.push(tables) + } else { + var db_tools = $("input[value^='dbtools_']"); + for (var i = 0; i < db_tools.length; i++) { + if (db_tools[i].checked) dbs.push(db_tools[i].value.replace('dbtools_', '')); + } + } + + if (dbs.length < 1) { + layer.msg('请至少选择一张表!', { icon: 2 }); + return false; + } + return dbs; +} + +function repDatabase(db_name, tables) { + dbs = repCheckeds(tables); + + myPost('repair_table', { db_name: db_name, tables: JSON.stringify(dbs) }, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); + repTools(db_name, true); + },'已送修复指令,请稍候...'); +} + + +function optDatabase(db_name, tables) { + dbs = repCheckeds(tables); + + myPost('opt_table', { db_name: db_name, tables: JSON.stringify(dbs) }, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); + repTools(db_name, true); + },'已送优化指令,请稍候...'); +} + +function toDatabaseType(db_name, tables, type){ + dbs = repCheckeds(tables); + myPost('alter_table', { db_name: db_name, tables: JSON.stringify(dbs),table_type: type }, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); + repTools(db_name, true); + }, '已送引擎转换指令,请稍候...'); +} + + +function selectedTools(my_obj, db_name) { + var is_checked = false + + if (my_obj) is_checked = my_obj.checked; + var db_tools = $("input[value^='dbtools_']"); + var n = 0; + for (var i = 0; i < db_tools.length; i++) { + if (my_obj) db_tools[i].checked = is_checked; + if (db_tools[i].checked) n++; + } + if (n > 0) { + var my_btns = '\ + \ + \ + ' + $("#db_tools").html(my_btns); + } else { + $("#db_tools").html(''); + } +} + +function repTools(db_name, res){ + myPost('get_db_info', {name:db_name}, function(data){ + var rdata = $.parseJSON(data.data); + var types = { InnoDB: "MyISAM", MyISAM: "InnoDB" }; + var tbody = ''; + for (var i = 0; i < rdata.tables.length; i++) { + if (!types[rdata.tables[i].type]) continue; + tbody += '\ + \ + ' + rdata.tables[i].table_name + '\ + ' + rdata.tables[i].type + '\ + ' + rdata.tables[i].collation + '\ + ' + rdata.tables[i].rows_count + '\ + ' + rdata.tables[i].data_size + '\ + \ + 修复 |\ + 优化 |\ + 转为' + types[rdata.tables[i].type] + '\ + \ + ' + } + + if (res) { + $(".gztr").html(tbody); + $("#db_tools").html(''); + $("input[type='checkbox']").attr("checked", false); + $(".tools_size").html('大小:' + rdata.data_size); + return; + } + + layer.open({ + type: 1, + title: "MySQL工具箱【" + db_name + "】", + area: ['780px', '580px'], + closeBtn: 2, + shadeClose: false, + content: '
\ + \ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + ' + tbody + '\ +
表名引擎字符集行数大小操作
\ +
\ +
\ +
    \ +
  • 【修复】尝试使用REPAIR命令修复损坏的表,仅能做简单修复,若修复不成功请考虑使用myisamchk工具
  • \ +
  • 【优化】执行OPTIMIZE命令,可回收未释放的磁盘空间,建议每月执行一次
  • \ +
  • 【转为InnoDB/MyISAM】转换数据表引擎,建议将所有表转为InnoDB
  • \ +
' + }); + tableFixed('database_fix'); + }); +} + + +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 setDbSlave(name){ + myPost('set_db_slave', {name:name}, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){ + masterOrSlaveConf(); + }, 2000); + }); +} + + +function addMasterRepSlaveUser(){ + layer.open({ + type: 1, + area: '500px', + title: '添加同步账户', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["提交","取消"], + content: "
\ +
用户名
\ +
\ + 密码\ +
\ +
\ + \ +
", + success:function(){ + $("input[name='name']").keyup(function(){ + var v = $(this).val(); + $("input[name='db_user']").val(v); + $("input[name='ps']").val(v); + }); + + $('select[name="dataAccess"]').change(function(){ + var v = $(this).val(); + if (v == 'ip'){ + $(this).after(""); + } else { + $('#dataAccess_subid').remove(); + } + }); + }, + yes:function(index){ + var data = $("#add_master").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['address']){ + dataObj['address'] = dataObj['dataAccess']; + } + + myPost('add_master_rep_slave_user', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + if (rdata.status){ + getMasterRepSlaveList(); + } + },{icon: rdata.status ? 1 : 2},600); + }); + } + }); +} + + + +function updateMasterRepSlaveUser(username){ + + var index = layer.open({ + type: 1, + area: '500px', + title: '更新账户', + closeBtn: 1, + shift: 5, + shadeClose: true, + content: "
\ +
用户名
\ +
\ + 密码\ +
\ +
\ + \ +
\ + \ +
\ +
", + }); + + $('#submit_update_master').click(function(){ + var data = $("#update_master").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + myPost('update_master_rep_slave_user', data, 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, db=''){ + myPost('get_master_rep_slave_user_cmd', {username:username,db:db}, function(data){ + var rdata = $.parseJSON(data.data); + + if (!rdata['status']){ + layer.msg(rdata['msg']); + return; + } + + var cmd = rdata.data['cmd']; + + var loadOpen = layer.open({ + type: 1, + title: '同步命令', + area: '500px', + content:"
\ +
"+cmd+"
\ +
\ + \ +
\ +
", + }); + + + copyPass(cmd); + $('.class-copy-cmd').click(function(){ + copyPass(cmd); + }); + }); +} + +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 setDbMasterAccess(username){ + myPost('get_db_access','username='+username, function(data){ + var rdata = $.parseJSON(data.data); + if (!rdata.status){ + layer.msg(rdata.msg,{icon:2,shade: [0.3, '#000']}); + return; + } + + var index = layer.open({ + type: 1, + area: '500px', + title: '设置数据库权限', + closeBtn: 1, + shift: 5, + btn:["提交","取消"], + shadeClose: true, + content: "
\ +
\ + 访问权限\ +
\ + \ +
\ +
\ +
", + success:function(){ + if (rdata.msg == '127.0.0.1'){ + $('select[name="dataAccess"]').find("option[value='127.0.0.1']").attr("selected",true); + } else if (rdata.msg == '%'){ + $('select[name="dataAccess"]').find('option[value="%"]').attr("selected",true); + } else if ( rdata.msg == 'ip' ){ + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } else { + $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true); + $('select[name="dataAccess"]').after(""); + } + + $('select[name="dataAccess"]').change(function(){ + var v = $(this).val(); + if (v == 'ip'){ + $(this).after(""); + } else { + $('#dataAccess_subid').remove(); + } + }); + }, + yes:function(index){ + var data = $("#set_db_access").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['access']){ + dataObj['access'] = dataObj['dataAccess']; + if ( dataObj['dataAccess'] == 'ip'){ + if (dataObj['address']==''){ + layer.msg('IP地址不能空!',{icon:2,shade: [0.3, '#000']}); + return; + } + dataObj['access'] = dataObj['address']; + } + } + dataObj['username'] = username; + myPost('set_dbmaster_access', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(index); + },{icon: rdata.status ? 1 : 2}); + }); + } + }); + + }); +} + +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']+'\ + \ + 修改 | \ + 删除 | \ + 权限 | \ + 从库同步命令\ + \ + '; + } + + $('#get_master_rep_slave_list_page tbody').html(list); + $('.dataTables_paginate_4').html(rdata['page']); + }); +} + +function getMasterRepSlaveListPage(){ + var page = '
'; + page += '
添加同步账户
'; + + var loadOpen = layer.open({ + type: 1, + title: '同步账户列表', + area: '500px', + content:"
\ +
\ +
\ + \ + \ +
用户名密码操作
\ + "+page +"\ +
\ +
", + success:function(){ + getMasterRepSlaveList(); + } + }); +} + + +function deleteSlave(){ + myPost('delete_slave', {}, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata['msg'], function(){ + masterOrSlaveConf(); + },{},3000); + }); +} + + +function getFullSyncStatus(db){ + var timeId = null; + + var btn = '
开始
'; + var loadOpen = layer.open({ + type: 1, + title: '全量同步['+db+']', + area: '500px', + content:"
\ +
\ + \ +
\ +
0%
\ +
\ +
\ + "+btn+"\ +
", + cancel: function(){ + clearInterval(timeId); + } + }); + + function fullSync(db,begin){ + + myPostN('full_sync', {db:db,begin:begin}, function(data){ + var rdata = $.parseJSON(data.data); + $('#full_msg').text(rdata['msg']); + $('.progress-bar').css('width',rdata['progress']+'%'); + $('.progress-bar').text(rdata['progress']+'%'); + + if (rdata['code']==6 ||rdata['code']<0){ + layer.msg(rdata['msg']); + clearInterval(timeId); + $("#begin_full_sync").attr('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=''){ + + myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){ + + var rdata = $.parseJSON(rdata.data); + + var ip = '127.0.0.1'; + var port = "22"; + var id_rsa = ''; + var db_user =''; + + if (rdata.data.length>0){ + ip = rdata.data[0]['ip']; + port = rdata.data[0]['port']; + id_rsa = rdata.data[0]['id_rsa']; + db_user = rdata.data[0]['db_user']; + } + + var index = layer.open({ + type: 1, + area: ['500px','480px'], + title: '添加SSH', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["确认","取消"], + content: "
\ +
IP
\ +
端口
\ +
同步账户[DB]
\ +
\ + ID_RSA\ +
\ +
\ + \ +
", + success:function(){ + $('textarea[name="id_rsa"]').html(id_rsa); + }, + yes:function(index){ + var ip = $('input[name="ip"]').val(); + var port = $('input[name="port"]').val(); + var db_user = $('input[name="db_user"]').val(); + var id_rsa = $('textarea[name="id_rsa"]').val(); + + var data = {ip:ip,port:port,id_rsa:id_rsa,db_user:db_user}; + myPost('add_slave_ssh', data, function(data){ + layer.close(index); + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + if (rdata.status){ + getSlaveSSHPage(); + } + },{icon: rdata.status ? 1 : 2},600); + }); + } + }); + }); +} + + +function delSlaveSSH(ip){ + myPost('del_slave_ssh', {ip:ip}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + getSlaveSSHPage(); + }); +} + +function getSlaveSSHPage(page=1){ + var _data = {}; + _data['page'] = page; + _data['page_size'] = 5; + _data['tojs'] ='getSlaveSSHPage'; + myPost('get_slave_ssh_list', _data, function(data){ + var layerId = null; + var rdata = []; + try { + rdata = $.parseJSON(data.data); + } catch(e) { + console.log(e); + } + var list = ''; + var ssh_list = rdata['data']; + for (i in ssh_list) { + var ip = ssh_list[i]['ip']; + var port = ssh_list[i]['port']; + + var id_rsa = '未设置'; + if ( ssh_list[i]['port'] != ''){ + id_rsa = '已设置'; + } + + var db_user = '未设置'; + if ( ssh_list[i]['db_user'] != ''){ + db_user = ssh_list[i]['db_user']; + } + + list += ''+ip+'\ + '+port+'\ + '+db_user+'\ + '+id_rsa+'\ + \ + 修改 | \ + 删除\ + \ + '; + } + + $('.get-slave-ssh-list tbody').html(list); + $('.dataTables_paginate_4').html(rdata['page']); + }); +} + + +function getSlaveSSHList(page=1){ + + var page = '
'; + page += '
添加SSH
'; + + layerId = layer.open({ + type: 1, + title: 'SSH列表', + area: '500px', + content:"
\ +
\ +
\ + \ + \ +
IPPORT同步账户SSH操作
\ + "+page +"\ +
\ +
", + success:function(){ + getSlaveSSHPage(1); + } + }); +} + +function handlerRun(){ + myPostN('get_slave_sync_cmd', {}, function(data){ + var rdata = $.parseJSON(data.data); + var cmd = rdata['data']; + var loadOpen = layer.open({ + type: 1, + title: '手动执行', + area: '500px', + content:"
\ +
"+cmd+"
\ +
\ + \ +
\ +
", + }); + copyPass(cmd); + $('.class-copy-cmd').click(function(){ + copyPass(cmd); + }); + }); +} + +function initSlaveStatus(){ + myPost('init_slave_status', '', function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + if (rdata.status){ + masterOrSlaveConf(); + } + },{icon:rdata.status?1:2},2000); + }); +} + +function masterOrSlaveConf(version=''){ + + function getMasterDbList(){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + + myPost('get_masterdb_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + var list = ''; + for(i in rdata.data){ + list += ''; + list += '' + rdata.data[i]['name'] +''; + list += '' + (rdata.data[i]['master']?'是':'否') +''; + list += '' + + ''+(rdata.data[i]['master']?'退出':'加入')+' | ' + + '同步命令' + + ''; + list += ''; + } + + var con = '
\ +
\ + \ + \ + \ + \ + \ + \ + '+ list +'\ +
数据库名同步操作
\ +
\ +
\ +
\ + 同步账户列表\ +
\ +
'; + + $(".table_master_list").html(con); + $('#databasePage').html(rdata.page); + }); + } + + function getAsyncMasterDbList(){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + + myPost('get_slave_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + var list = ''; + for(i in rdata.data){ + + var v = rdata.data[i]; + var status = "异常"; + if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ + status = "正常"; + } + + list += ''; + list += '' + rdata.data[i]['Master_Host'] +''; + list += '' + rdata.data[i]['Master_Port'] +''; + list += '' + rdata.data[i]['Master_User'] +''; + list += '' + rdata.data[i]['Master_Log_File'] +''; + list += '' + rdata.data[i]['Slave_IO_Running'] +''; + list += '' + rdata.data[i]['Slave_SQL_Running'] +''; + list += '' + status +''; + list += '' + + '删除' + + ''; + list += ''; + } + + var con = '
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + '+ list +'\ +
主[服务]端口用户日志IOSQL状态操作
\ +
\ +
'; + + //
\ + //
\ + // 添加\ + //
+ $(".table_slave_status_list").html(con); + }); + } + + function getAsyncDataList(){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + myPost('get_masterdb_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + var list = ''; + for(i in rdata.data){ + list += ''; + list += '' + rdata.data[i]['name'] +''; + list += '' + + ''+(rdata.data[i]['slave']?'退出':'加入')+' | ' + + '同步' + + ''; + list += ''; + } + + var con = '
\ +
\ + \ + \ + \ + \ + \ + '+ list +'\ +
本地库名操作
\ +
\ +
\ +
\ + 手动命令\ + 全量同步\ +
\ +
'; + + $(".table_slave_list").html(con); + $('#databasePage').html(rdata.page); + }); + } + + + + function getMasterStatus(){ + myPost('get_master_status', '', function(data){ + var rdata = $.parseJSON(data.data); + // console.log('mode:',rdata.data); + var rdata = rdata.data; + var limitCon = '\ +

\ + 主从同步模式\ + \ + \ +

\ +
\ +

\ + Master[主]配置\ + \ +

\ +
\ + \ +
\ +
\ + \ +

\ + Slave[从]配置\ + \ + \ + \ +

\ +
\ + \ +
\ + \ +
\ + '; + $(".soft-man-con").html(limitCon); + + //设置主服务器配置 + $(".btn-master").click(function () { + myPost('set_master_status', 'close=change', function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){ + getMasterStatus(); + }, 3000); + }); + }); + + $(".btn-slave").click(function () { + myPost('set_slave_status', 'close=change', function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){ + getMasterStatus(); + }, 3000); + }); + }); + + $('.db-mode').click(function(){ + if ($(this).hasClass('btn-success')){ + //no action + return; + } + + var mode = 'classic'; + if ($(this).hasClass('btn-gtid')){ + mode = 'gtid'; + } + + layer.open({ + type:1, + title:"MySQL主从模式切换", + shadeClose:false, + btnAlign: 'c', + btn: ['切换并重启', '切换不重启'], + yes: function(index, layero){ + this.change(index,mode,"yes"); + + }, + btn2: function(index, layero){ + this.change(index,mode,"no"); + return false; + }, + change:function(index,mode,reload){ + console.log(index,mode,reload); + myPost('set_dbrun_mode',{'mode':mode,'reload':reload},function(data){ + layer.close(index); + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg ,function(){ + getMasterStatus(); + },{ icon: rdata.status ? 1 : 5 }); + }); + } + }); + }); + + if (rdata.status){ + getMasterDbList(); + } + + if (rdata.slave_status){ + getAsyncMasterDbList(); + getAsyncDataList() + } + }); + } + getMasterStatus(); +} diff --git a/plugins/postgresql/versions/14.4/install.sh b/plugins/postgresql/versions/14.4/install.sh new file mode 100755 index 000000000..6dd3a80b8 --- /dev/null +++ b/plugins/postgresql/versions/14.4/install.sh @@ -0,0 +1,101 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +#https://www.postgresql.org/ftp/source/ + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sysName=`uname` + +install_tmp=${rootPath}/tmp/mw_install.pl +postgreDir=${serverPath}/source/postgresql + +VERSION=14.4 + +Install_App() +{ + mkdir -p ${postgreDir} + echo '正在安装脚本文件...' > $install_tmp + + if id postgresql &> /dev/null ;then + echo "postgresql UID is `id -u postgresql`" + echo "postgresql Shell is `grep "^postgresql:" /etc/passwd |cut -d':' -f7 `" + else + groupadd postgresql + useradd -g postgresql postgresql + fi + + if [ "$sysName" != "Darwin" ];then + mkdir -p /var/log/mariadb + touch /var/log/mariadb/mariadb.log + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + # for stable installation + if [ "$cpuCore" -gt "1" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + fi + # ----- cpu end ------ + + if [ ! -f ${postgreDir}/postgresql-${VERSION}.tar.bz2 ];then + wget --no-check-certificate -O ${postgreDir}/postgresql-${VERSION}.tar.bz2 --tries=3 https://ftp.postgresql.org/pub/source/v${VERSION}/postgresql-${VERSION}.tar.bz2 + fi + + if [ ! -d ${postgreDir}/postgresql-${VERSION} ];then + cd ${postgreDir} && tar -jxvf ${postgreDir}/postgresql-${VERSION}.tar.bz2 + fi + + + if [ ! -d $serverPath/postgresql ];then + cd ${postgreDir}/postgresql-${VERSION} && ./configure \ + --prefix=$serverPath/postgresql + # --with-openssl \ + # --with-pgport=33206 + + echo "cd ${postgreDir}/postgresql-${VERSION} && ./configure \ + --prefix=$serverPath/postgresql" + # --with-openssl \ + # --with-pgport=33206 + make -j${cpuCore} && make install && make clean + fi + + if [ -d $serverPath/postgresql ];then + echo "${VERSION}" > $serverPath/postgresql/version.pl + echo 'install successful' > $install_tmp + else + echo 'install fail' > $install_tmp + fi +} + +Uninstall_App() +{ + rm -rf $serverPath/postgresql + echo '卸载完成' > $install_tmp +} + +action=$1 +if [ "${1}" == "install" ];then + Install_App +else + Uninstall_App +fi diff --git a/route/static/app/crontab.js b/route/static/app/crontab.js index 459e65d50..3509bdcf4 100755 --- a/route/static/app/crontab.js +++ b/route/static/app/crontab.js @@ -35,7 +35,7 @@ function getLogs(id){ }); setTimeout(function(){ - $("#crontab-log").text(rdata.msg); + $("#crontab-log").html(rdata.msg); },200); },'json'); } diff --git a/scripts/backup.py b/scripts/backup.py index 317f0a10b..12f35bbd6 100755 --- a/scripts/backup.py +++ b/scripts/backup.py @@ -112,8 +112,14 @@ class backupTools: if len(mycnf) > 100: mw.writeFile(db_path + '/etc/my.cnf', mycnf) - mw.execShell( - db_path + "/bin/mysqldump --opt --default-character-set=utf8 " + name + " | gzip > " + filename) + # mw.execShell(db_path + "/bin/mysqldump --opt --default-character-set=utf8 " + + # name + " | gzip > " + filename) + + # mw.execShell(db_path + "/bin/mysqldump --skip-lock-tables --default-character-set=utf8 " + + # name + " | gzip > " + filename) + + mw.execShell(db_path + "/bin/mysqldump --single-transaction --quick --default-character-set=utf8 " + + name + " | gzip > " + filename) if not os.path.exists(filename): endDate = time.strftime('%Y/%m/%d %X', time.localtime()) @@ -135,7 +141,7 @@ class backupTools: mw.M('backup').add('type,name,pid,filename,addtime,size', (1, os.path.basename( filename), pid, filename, endDate, os.path.getsize(filename))) - log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + u"]秒" + log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒" mw.writeLog('计划任务', log) print("★[" + endDate + "] " + log) print("|---保留最新的[" + count + "]份备份") diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index 1dfbc7984..7d660dc77 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -3,7 +3,7 @@ # description: MW Cloud Service ### BEGIN INIT INFO -# Provides: bt +# Provides: Midoks # Required-Start: $all # Required-Stop: $all # Default-Start: 2 3 4 5 diff --git a/scripts/install.sh b/scripts/install.sh index 91e78049e..e5d11d439 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -58,8 +58,18 @@ if [ $OSNAME != "macos" ];then mkdir -p /www/backup/database mkdir -p /www/backup/site + + # https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh + if [ ! -d /www/server/mdserver-web ];then - wget -O /tmp/master.zip https://codeload.github.com/midoks/mdserver-web/zip/master + + cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"") + if [ ! -z "$cn" ];then + wget -O /tmp/master.zip https://gitee.com/midoks/mdserver-web/repository/archive/master.zip + else + wget -O /tmp/master.zip https://codeload.github.com/midoks/mdserver-web/zip/master + fi + cd /tmp && unzip /tmp/master.zip mv -f /tmp/mdserver-web-master /www/server/mdserver-web rm -rf /tmp/master.zip diff --git a/scripts/lib.sh b/scripts/lib.sh index 8ed34152b..6c491a156 100755 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -113,7 +113,7 @@ cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirem pip3 install gevent-websocket==0.10.1 pip3 install flask-caching==1.10.1 -pip3 install mysqlclient +# pip3 install mysqlclient if [ ! -f /www/server/mdserver-web/bin/activate ];then @@ -129,5 +129,5 @@ pip3 install -r /www/server/mdserver-web/requirements.txt pip3 install gevent-websocket==0.10.1 pip3 install flask-caching==1.10.1 -pip3 install mysqlclient +# pip3 install mysqlclient diff --git a/scripts/update.sh b/scripts/update.sh index dd13727bb..9e6439da2 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -46,7 +46,15 @@ else OSNAME='unknow' fi -wget -O /tmp/master.zip https://codeload.github.com/midoks/mdserver-web/zip/master + +cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"") +if [ ! -z "$cn" ];then + wget -O /tmp/master.zip https://gitee.com/midoks/mdserver-web/repository/archive/master.zip +else + wget -O /tmp/master.zip https://codeload.github.com/midoks/mdserver-web/zip/master +fi + + cd /tmp && unzip /tmp/master.zip /usr/bin/cp -rf /tmp/mdserver-web-master/* /www/server/mdserver-web rm -rf /tmp/master.zip