From 4d9b5508859f259e5fc403727cf5df49ade627e2 Mon Sep 17 00:00:00 2001 From: quanbisen <1769128867@qq.com> Date: Sat, 1 Apr 2023 15:54:54 +0800 Subject: [PATCH 1/9] add mongo db type management support --- sql/engines/__init__.py | 20 ++ sql/engines/mongo.py | 97 ++++++ sql/engines/mysql.py | 102 +++++++ sql/instance_account.py | 276 ++++++++--------- sql/instance_database.py | 63 ++-- sql/models.py | 3 +- sql/templates/database.html | 121 +++++--- sql/templates/instanceaccount.html | 468 +++++++++++++++++++++-------- sql/utils/instance_management.py | 19 ++ 9 files changed, 817 insertions(+), 352 deletions(-) create mode 100644 sql/utils/instance_management.py diff --git a/sql/engines/__init__.py b/sql/engines/__init__.py index a101abed13..cbdef337d7 100644 --- a/sql/engines/__init__.py +++ b/sql/engines/__init__.py @@ -132,6 +132,26 @@ def get_tables_metas_data(self, db_name, **kwargs): """获取数据库所有表格信息,用作数据字典导出接口""" return list() + def get_all_databases_summary(self): + """实例数据库管理功能,获取实例所有的数据库描述信息""" + return ResultSet() + + def get_instance_users_summary(self): + """实例账号管理功能,获取实例所有账号信息""" + return ResultSet() + + def create_instance_user(self, **kwargs): + """实例账号管理功能,创建实例账号""" + return ResultSet() + + def drop_instance_user(self, **kwargs): + """实例账号管理功能,删除实例账号""" + return ResultSet() + + def reset_instance_user_pwd(self, **kwargs): + """实例账号管理功能,重置实例账号密码""" + return ResultSet() + def get_all_columns_by_tb(self, db_name, tb_name, **kwargs): """获取所有字段, 返回一个ResultSet,rows=list""" return ResultSet() diff --git a/sql/engines/mongo.py b/sql/engines/mongo.py index 7a0f480a42..23587a1962 100644 --- a/sql/engines/mongo.py +++ b/sql/engines/mongo.py @@ -1270,3 +1270,100 @@ def kill_op(self, opids): f"mongodb语句执行killOp报错,语句:db.runCommand({sql}) ,错误信息{traceback.format_exc()}" ) result.error = str(e) + + def get_all_databases_summary(self): + """实例数据库管理功能,获取实例所有的数据库描述信息""" + query_result = self.get_all_databases() + if not query_result.error: + dbs = query_result.rows + conn = self.get_connection() + + # 获取数据库用户信息 + rows = [] + for db_name in dbs: + # 执行语句 + listing = conn[db_name].command(command='usersInfo') + grantees = [] + for user_obj in listing['users']: + grantees.append({ + 'user': user_obj['user'], + 'roles': user_obj['roles'] + }.__str__()) + row = { + "db_name": db_name, + "grantees": grantees, + "saved": False, + } + rows.append(row) + query_result.rows = rows + return query_result + + def get_instance_users_summary(self): + """实例账号管理功能,获取实例所有账号信息""" + query_result = self.get_all_databases() + if not query_result.error: + dbs = query_result.rows + conn = self.get_connection() + + # 获取数据库用户信息 + rows = [] + for db_name in dbs: + # 执行语句 + listing = conn[db_name].command(command='usersInfo') + for user_obj in listing['users']: + rows.append({ + "db_name_user": f"{db_name}.{user_obj['user']}", + "db_name": db_name, + "user": user_obj['user'], + "roles": [role['role'] for role in user_obj['roles']], + "saved": False, + }) + query_result.rows = rows + return query_result + + def create_instance_user(self, **kwargs): + """实例账号管理功能,创建实例账号""" + exec_result = ResultSet() + db_name = kwargs.get("db_name", "") + user = kwargs.get("user", "") + password1 = kwargs.get("password1", "") + remark = kwargs.get("remark", "") + try: + conn = self.get_connection() + conn[db_name].command("createUser", user, pwd=password1, roles=[]) + exec_result.rows = [{ + "instance": self.instance, + "db_name": db_name, + "user": user, + "password": password1, + "remark": remark + }] + except Exception as e: + exec_result.error = str(e) + return exec_result + + def drop_instance_user(self, db_name_user: str, **kwarg): + """实例账号管理功能,删除实例账号""" + arr = db_name_user.split(".") + db_name = arr[0] + user = arr[1] + exec_result = ResultSet() + try: + conn = self.get_connection() + conn[db_name].command("dropUser", user) + except Exception as e: + exec_result.error = str(e) + return exec_result + + def reset_instance_user_pwd(self, db_name_user: str, reset_pwd: str, **kwargs): + """实例账号管理功能,重置实例账号密码""" + arr = db_name_user.split(".") + db_name = arr[0] + user = arr[1] + exec_result = ResultSet() + try: + conn = self.get_connection() + conn[db_name].command("updateUser", user, pwd=reset_pwd) + except Exception as e: + exec_result.error = str(e) + return exec_result diff --git a/sql/engines/mysql.py b/sql/engines/mysql.py index ee47fd4240..9d42af6c3b 100644 --- a/sql/engines/mysql.py +++ b/sql/engines/mysql.py @@ -282,6 +282,108 @@ def get_tables_metas_data(self, db_name, **kwargs): table_metas.append(_meta) return table_metas + def get_all_databases_summary(self): + """实例数据库管理功能,获取实例所有的数据库描述信息""" + # 获取所有数据库 + sql_get_db = """SELECT SCHEMA_NAME,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME + FROM information_schema.SCHEMATA + WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'test', 'sys');""" + query_result = self.query( + "information_schema", sql_get_db, close_conn=False + ) + if not query_result.error: + dbs = query_result.rows + # 获取数据库关联用户信息 + rows = [] + for db in dbs: + db_name = db[0] + sql_get_bind_users = f"""select group_concat(distinct(GRANTEE)),TABLE_SCHEMA + from information_schema.SCHEMA_PRIVILEGES + where TABLE_SCHEMA='{db_name}' + group by TABLE_SCHEMA;""" + bind_users = self.query( + "information_schema", sql_get_bind_users, close_conn=False + ).rows + row = { + "db_name": db_name, + "charset": db[1], + "collation": db[2], + "grantees": bind_users[0][0].split(",") if bind_users else [], + "saved": False, + } + rows.append(row) + query_result.rows = rows + return query_result + + def get_instance_users_summary(self): + """实例账号管理功能,获取实例所有账号信息""" + server_version = self.server_version + # MySQL 5.7.6版本起支持ACCOUNT LOCK + if server_version >= (5, 7, 6): + sql_get_user = "select concat('`', user, '`', '@', '`', host,'`') as query,user,host,account_locked from mysql.user;" + else: + sql_get_user = "select concat('`', user, '`', '@', '`', host,'`') as query,user,host from mysql.user;" + query_result = self.query("mysql", sql_get_user) + if not query_result.error: + db_users = query_result.rows + # 获取用户权限信息 + rows = [] + for db_user in db_users: + user_host = db_user[0] + user_priv = self.query( + "mysql", "show grants for {};".format(user_host), close_conn=False + ).rows + row = { + "user_host": user_host, + "user": db_user[1], + "host": db_user[2], + "privileges": user_priv, + "saved": False, + "is_locked": db_user[3] if server_version >= (5, 7, 6) else None, + } + rows.append(row) + query_result.rows = rows + return query_result + + def create_instance_user(self, **kwargs): + """实例账号管理功能,创建实例账号""" + # escape + user = MySQLdb.escape_string(kwargs.get("user", "")).decode("utf-8") + host = MySQLdb.escape_string(kwargs.get("host", "")).decode("utf-8") + password1 = MySQLdb.escape_string(kwargs.get("password1", "")).decode("utf-8") + remark = kwargs.get("remark", "") + # 在一个事务内执行 + hosts = host.split("|") + create_user_cmd = "" + accounts = [] + for host in hosts: + create_user_cmd += f"create user '{user}'@'{host}' identified by '{password1}';" + accounts.append({ + "instance": self.instance, + "user": user, + "host": host, + "password": password1, + "remark": remark + }) + exec_result = self.execute(db_name="mysql", sql=create_user_cmd) + exec_result.rows = accounts + return exec_result + + def drop_instance_user(self, user_host: str, **kwarg): + """实例账号管理功能,删除实例账号""" + # escape + user_host = MySQLdb.escape_string(user_host).decode("utf-8") + return self.execute(db_name="mysql", sql=f"DROP USER {user_host};") + + def reset_instance_user_pwd(self, user_host: str, reset_pwd: str, **kwargs): + """实例账号管理功能,重置实例账号密码""" + # escape + user_host = MySQLdb.escape_string(user_host).decode("utf-8") + reset_pwd = MySQLdb.escape_string(reset_pwd).decode("utf-8") + return self.execute( + db_name="mysql", sql=f"ALTER USER {user_host} IDENTIFIED BY '{reset_pwd}';" + ) + def get_all_columns_by_tb(self, db_name, tb_name, **kwargs): """获取所有字段, 返回一个ResultSet""" sql = f"""SELECT diff --git a/sql/instance_account.py b/sql/instance_account.py index 06b71fe961..20c0f406ae 100644 --- a/sql/instance_account.py +++ b/sql/instance_account.py @@ -6,9 +6,10 @@ from django.core.exceptions import ValidationError from django.http import HttpResponse, JsonResponse - +from sql.utils.instance_management import SUPPORTED_MANAGEMENT_DB_TYPE, \ + get_instanceaccount_unique_value, get_instanceaccount_unique_key from common.utils.extend_json_encoder import ExtendJSONEncoder -from sql.engines import get_engine +from sql.engines import get_engine, ResultSet from sql.utils.resource_group import user_instances from .models import Instance, InstanceAccount @@ -22,46 +23,27 @@ def users(request): if not instance_id: return JsonResponse({"status": 0, "msg": "", "data": []}) try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=SUPPORTED_MANAGEMENT_DB_TYPE).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) # 获取已录入用户 cnf_users = dict() for user in InstanceAccount.objects.filter(instance=instance).values( - "id", "user", "host", "remark" + "id", "user", "host", "db_name", "remark" ): user["saved"] = True - cnf_users[f"`{user['user']}`@`{user['host']}`"] = user + cnf_users[get_instanceaccount_unique_value(instance.db_type, user)] = user # 获取所有用户 query_engine = get_engine(instance=instance) - server_version = query_engine.server_version - # MySQL 5.7.6版本起支持ACCOUNT LOCK - if server_version >= (5, 7, 6): - sql_get_user = "select concat('`', user, '`', '@', '`', host,'`') as query,user,host,account_locked from mysql.user;" - else: - sql_get_user = "select concat('`', user, '`', '@', '`', host,'`') as query,user,host from mysql.user;" - query_result = query_engine.query("mysql", sql_get_user) + query_result = query_engine.get_instance_users_summary() if not query_result.error: - db_users = query_result.rows - # 获取用户权限信息 rows = [] - for db_user in db_users: - user_host = db_user[0] - user_priv = query_engine.query( - "mysql", "show grants for {};".format(user_host), close_conn=False - ).rows - row = { - "user_host": user_host, - "user": db_user[1], - "host": db_user[2], - "privileges": user_priv, - "saved": False, - "is_locked": db_user[3] if server_version >= (5, 7, 6) else None, - } + key = get_instanceaccount_unique_key(db_type=instance.db_type) + for row in query_result.rows: # 合并数据 - if user_host in cnf_users.keys(): - row = dict(row, **cnf_users[user_host]) + if row[key] in cnf_users.keys(): + row = dict(row, **cnf_users[row[key]]) rows.append(row) # 过滤参数 if saved: @@ -83,6 +65,7 @@ def users(request): def create(request): """创建数据库账号""" instance_id = request.POST.get("instance_id", 0) + db_name = request.POST.get("db_name") user = request.POST.get("user") host = request.POST.get("host") password1 = request.POST.get("password1") @@ -90,11 +73,12 @@ def create(request): remark = request.POST.get("remark", "") try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=SUPPORTED_MANAGEMENT_DB_TYPE).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) - if not all([user, host, password1, password2]): + if (instance.db_type == 'mysql' and not all([user, host, password1, password2])) or \ + (instance.db_type == 'mongo' and not all([db_name, user, password1, password2])): return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) if password1 != password2: @@ -106,34 +90,18 @@ def create(request): except ValidationError as msg: return JsonResponse({"status": 1, "msg": f"{msg}", "data": []}) - # escape - user = MySQLdb.escape_string(user).decode("utf-8") - host = MySQLdb.escape_string(host).decode("utf-8") - password1 = MySQLdb.escape_string(password1).decode("utf-8") - engine = get_engine(instance=instance) - # 在一个事务内执行 - hosts = host.split("|") - create_user_cmd = "" - accounts = [] - for host in hosts: - create_user_cmd += f"create user '{user}'@'{host}' identified by '{password1}';" - accounts.append( - InstanceAccount( - instance=instance, - user=user, - host=host, - password=password1, - remark=remark, - ) - ) - exec_result = engine.execute(db_name="mysql", sql=create_user_cmd) + exec_result = engine.create_instance_user(db_name=db_name, user=user, host=host, password1=password1, remark=remark) + # 关闭连接 + engine.close() if exec_result.error: return JsonResponse({"status": 1, "msg": exec_result.error}) # 保存到数据库 else: + accounts = [InstanceAccount(**row) for row in exec_result.rows] InstanceAccount.objects.bulk_create(accounts) + return JsonResponse({"status": 0, "msg": "", "data": []}) @@ -141,17 +109,19 @@ def create(request): def edit(request): """修改、录入数据库账号""" instance_id = request.POST.get("instance_id", 0) + db_name = request.POST.get("db_name", "") user = request.POST.get("user") - host = request.POST.get("host") + host = request.POST.get("host", "") password = request.POST.get("password") remark = request.POST.get("remark", "") try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=SUPPORTED_MANAGEMENT_DB_TYPE).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) - if not all([user, host]): + if (instance.db_type == 'mysql' and not all([user, host])) or \ + (instance.db_type == 'mongo' and not all([db_name, user])): return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) # 保存到数据库 @@ -160,11 +130,12 @@ def edit(request): instance=instance, user=user, host=host, + db_name=db_name, defaults={"password": password, "remark": remark}, ) else: InstanceAccount.objects.update_or_create( - instance=instance, user=user, host=host, defaults={"remark": remark} + instance=instance, user=user, host=host, db_name=db_name, defaults={"remark": remark} ) return JsonResponse({"status": 0, "msg": "", "data": []}) @@ -173,77 +144,93 @@ def edit(request): def grant(request): """获取用户权限变更语句,并执行权限变更""" instance_id = request.POST.get("instance_id", 0) - user_host = request.POST.get("user_host") - op_type = int(request.POST.get("op_type")) - priv_type = int(request.POST.get("priv_type")) - privs = json.loads(request.POST.get("privs")) grant_sql = "" - # escape - user_host = MySQLdb.escape_string(user_host).decode("utf-8") - - # 全局权限 - if priv_type == 0: - global_privs = privs["global_privs"] - if not all([global_privs]): - return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) - global_privs = ["GRANT OPTION" if g == "GRANT" else g for g in global_privs] - if op_type == 0: - grant_sql = f"GRANT {','.join(global_privs)} ON *.* TO {user_host};" - elif op_type == 1: - grant_sql = f"REVOKE {','.join(global_privs)} ON *.* FROM {user_host};" - - # 库权限 - elif priv_type == 1: - db_privs = privs["db_privs"] - db_name = request.POST.getlist("db_name[]") - if not all([db_privs, db_name]): - return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) - for db in db_name: - db_privs = ["GRANT OPTION" if d == "GRANT" else d for d in db_privs] - if op_type == 0: - grant_sql += f"GRANT {','.join(db_privs)} ON `{db}`.* TO {user_host};" - elif op_type == 1: - grant_sql += ( - f"REVOKE {','.join(db_privs)} ON `{db}`.* FROM {user_host};" - ) - # 表权限 - elif priv_type == 2: - tb_privs = privs["tb_privs"] - db_name = request.POST.get("db_name") - tb_name = request.POST.getlist("tb_name[]") - if not all([tb_privs, db_name, tb_name]): - return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) - for tb in tb_name: - tb_privs = ["GRANT OPTION" if t == "GRANT" else t for t in tb_privs] - if op_type == 0: - grant_sql += ( - f"GRANT {','.join(tb_privs)} ON `{db_name}`.`{tb}` TO {user_host};" - ) - elif op_type == 1: - grant_sql += f"REVOKE {','.join(tb_privs)} ON `{db_name}`.`{tb}` FROM {user_host};" - # 列权限 - elif priv_type == 3: - col_privs = privs["col_privs"] - db_name = request.POST.get("db_name") - tb_name = request.POST.get("tb_name") - col_name = request.POST.getlist("col_name[]") - if not all([col_privs, db_name, tb_name, col_name]): - return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) - for priv in col_privs: - if op_type == 0: - grant_sql += f"GRANT {priv}(`{'`,`'.join(col_name)}`) ON `{db_name}`.`{tb_name}` TO {user_host};" - elif op_type == 1: - grant_sql += f"REVOKE {priv}(`{'`,`'.join(col_name)}`) ON `{db_name}`.`{tb_name}` FROM {user_host};" - - # 执行变更语句 try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=SUPPORTED_MANAGEMENT_DB_TYPE).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) engine = get_engine(instance=instance) - exec_result = engine.execute(db_name="mysql", sql=grant_sql) + if instance.db_type == 'mysql': + user_host = request.POST.get("user_host") + op_type = int(request.POST.get("op_type")) + priv_type = int(request.POST.get("priv_type")) + privs = json.loads(request.POST.get("privs")) + + # escape + user_host = MySQLdb.escape_string(user_host).decode("utf-8") + + # 全局权限 + if priv_type == 0: + global_privs = privs["global_privs"] + if not all([global_privs]): + return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) + global_privs = ["GRANT OPTION" if g == "GRANT" else g for g in global_privs] + if op_type == 0: + grant_sql = f"GRANT {','.join(global_privs)} ON *.* TO {user_host};" + elif op_type == 1: + grant_sql = f"REVOKE {','.join(global_privs)} ON *.* FROM {user_host};" + + # 库权限 + elif priv_type == 1: + db_privs = privs["db_privs"] + db_name = request.POST.getlist("db_name[]") + if not all([db_privs, db_name]): + return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) + for db in db_name: + db_privs = ["GRANT OPTION" if d == "GRANT" else d for d in db_privs] + if op_type == 0: + grant_sql += f"GRANT {','.join(db_privs)} ON `{db}`.* TO {user_host};" + elif op_type == 1: + grant_sql += ( + f"REVOKE {','.join(db_privs)} ON `{db}`.* FROM {user_host};" + ) + # 表权限 + elif priv_type == 2: + tb_privs = privs["tb_privs"] + db_name = request.POST.get("db_name") + tb_name = request.POST.getlist("tb_name[]") + if not all([tb_privs, db_name, tb_name]): + return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) + for tb in tb_name: + tb_privs = ["GRANT OPTION" if t == "GRANT" else t for t in tb_privs] + if op_type == 0: + grant_sql += ( + f"GRANT {','.join(tb_privs)} ON `{db_name}`.`{tb}` TO {user_host};" + ) + elif op_type == 1: + grant_sql += f"REVOKE {','.join(tb_privs)} ON `{db_name}`.`{tb}` FROM {user_host};" + # 列权限 + elif priv_type == 3: + col_privs = privs["col_privs"] + db_name = request.POST.get("db_name") + tb_name = request.POST.get("tb_name") + col_name = request.POST.getlist("col_name[]") + if not all([col_privs, db_name, tb_name, col_name]): + return JsonResponse({"status": 1, "msg": "信息不完整,请确认后提交", "data": []}) + for priv in col_privs: + if op_type == 0: + grant_sql += f"GRANT {priv}(`{'`,`'.join(col_name)}`) ON `{db_name}`.`{tb_name}` TO {user_host};" + elif op_type == 1: + grant_sql += f"REVOKE {priv}(`{'`,`'.join(col_name)}`) ON `{db_name}`.`{tb_name}` FROM {user_host};" + # 执行变更语句 + exec_result = engine.execute(db_name="mysql", sql=grant_sql) + elif instance.db_type == 'mongo': + db_name_user = request.POST.get("db_name_user") + roles = request.POST.getlist("roles[]") + arr = db_name_user.split(".") + db_name = arr[0] + user = arr[1] + exec_result = ResultSet() + try: + conn = engine.get_connection() + conn[db_name].command("updateUser", user, roles=roles) + except Exception as e: + exec_result.error = str(e) + + # 关闭连接 + engine.close() if exec_result.error: return JsonResponse({"status": 1, "msg": exec_result.error}) return JsonResponse({"status": 0, "msg": "", "data": grant_sql}) @@ -253,26 +240,25 @@ def grant(request): def reset_pwd(request): """创建数据库账号""" instance_id = request.POST.get("instance_id", 0) + db_name_user = request.POST.get("db_name_user") + db_name = request.POST.get("db_name", "") user_host = request.POST.get("user_host") user = request.POST.get("user") - host = request.POST.get("host") + host = request.POST.get("host", "") reset_pwd1 = request.POST.get("reset_pwd1") reset_pwd2 = request.POST.get("reset_pwd2") - if not all([user, host, reset_pwd1, reset_pwd2]): - return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) - - if reset_pwd1 != reset_pwd2: - return JsonResponse({"status": 1, "msg": "两次输入密码不一致", "data": []}) - try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=SUPPORTED_MANAGEMENT_DB_TYPE).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) - # escape - user_host = MySQLdb.escape_string(user_host).decode("utf-8") - reset_pwd1 = MySQLdb.escape_string(reset_pwd1).decode("utf-8") + if (instance.db_type == 'mysql' and not all([user, host, reset_pwd1, reset_pwd2])) or \ + (instance.db_type == 'mongo' and not all([db_name, user, reset_pwd1, reset_pwd2])): + return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) + + if reset_pwd1 != reset_pwd2: + return JsonResponse({"status": 1, "msg": "两次输入密码不一致", "data": []}) # TODO 目前使用系统自带验证,后续实现验证器校验 try: @@ -281,17 +267,18 @@ def reset_pwd(request): return JsonResponse({"status": 1, "msg": f"{msg}", "data": []}) engine = get_engine(instance=instance) - exec_result = engine.execute( - db_name="mysql", sql=f"ALTER USER {user_host} IDENTIFIED BY '{reset_pwd1}';" - ) + exec_result = engine.reset_instance_user_pwd(user_host=user_host, db_name_user=db_name_user, reset_pwd=reset_pwd1) + # 关闭连接 + engine.close() if exec_result.error: result = {"status": 1, "msg": exec_result.error} return HttpResponse(json.dumps(result), content_type="application/json") # 保存到数据库 else: InstanceAccount.objects.update_or_create( - instance=instance, user=user, host=host, defaults={"password": reset_pwd1} + instance=instance, user=user, host=host, db_name=db_name, defaults={"password": reset_pwd1} ) + return JsonResponse({"status": 0, "msg": "", "data": []}) @@ -330,26 +317,29 @@ def lock(request): def delete(request): """删除账号""" instance_id = request.POST.get("instance_id", 0) + db_name_user = request.POST.get("db_name_user") + db_name = request.POST.get("db_name") user_host = request.POST.get("user_host") user = request.POST.get("user") host = request.POST.get("host") - if not all([user_host]): - return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) - try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=SUPPORTED_MANAGEMENT_DB_TYPE).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) - # escape - user_host = MySQLdb.escape_string(user_host).decode("utf-8") + if (instance.db_type == 'mysql' and not all([user_host])) or \ + (instance.db_type == 'mongo' and not all([db_name_user])): + return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) engine = get_engine(instance=instance) - exec_result = engine.execute(db_name="mysql", sql=f"DROP USER {user_host};") + exec_result = engine.drop_instance_user(user_host=user_host, db_name_user=db_name_user) + # 关闭连接 + engine.close() if exec_result.error: return JsonResponse({"status": 1, "msg": exec_result.error}) # 删除数据库对应记录 else: - InstanceAccount.objects.filter(instance=instance, user=user, host=host).delete() + InstanceAccount.objects.filter(instance=instance, user=user, host=host, db_name=db_name).delete() + return JsonResponse({"status": 0, "msg": "", "data": []}) diff --git a/sql/instance_database.py b/sql/instance_database.py index 15d1572c35..b82ca15849 100644 --- a/sql/instance_database.py +++ b/sql/instance_database.py @@ -13,7 +13,7 @@ from django_redis import get_redis_connection from common.utils.extend_json_encoder import ExtendJSONEncoder -from sql.engines import get_engine +from sql.engines import get_engine, ResultSet from sql.models import Instance, InstanceDatabase, Users from sql.utils.resource_group import user_instances @@ -30,7 +30,7 @@ def databases(request): return JsonResponse({"status": 0, "msg": "", "data": []}) try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=["mysql", "mongo"]).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) @@ -42,42 +42,17 @@ def databases(request): db["saved"] = True cnf_dbs[f"{db['db_name']}"] = db - # 获取所有数据库 - sql_get_db = """SELECT SCHEMA_NAME,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME -FROM information_schema.SCHEMATA -WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'test', 'sys');""" query_engine = get_engine(instance=instance) - query_result = query_engine.query( - "information_schema", sql_get_db, close_conn=False - ) + query_result = query_engine.get_all_databases_summary() if not query_result.error: - dbs = query_result.rows # 获取数据库关联用户信息 rows = [] - for db in dbs: - db_name = db[0] - sql_get_bind_users = f"""select group_concat(distinct(GRANTEE)),TABLE_SCHEMA -from information_schema.SCHEMA_PRIVILEGES -where TABLE_SCHEMA='{db_name}' -group by TABLE_SCHEMA;""" - bind_users = query_engine.query( - "information_schema", sql_get_bind_users, close_conn=False - ).rows - row = { - "db_name": db_name, - "charset": db[1], - "collation": db[2], - "grantees": bind_users[0][0].split(",") if bind_users else [], - "saved": False, - } - # 合并数据 - if db_name in cnf_dbs.keys(): - row = dict(row, **cnf_dbs[db_name]) + for row in query_result.rows: + if row["db_name"] in cnf_dbs.keys(): + row = dict(row, **cnf_dbs[row["db_name"]]) rows.append(row) - # 过滤参数 if saved: rows = [row for row in rows if row["saved"]] - result = {"status": 0, "msg": "ok", "rows": rows} else: result = {"status": 1, "msg": query_result.error} @@ -102,7 +77,7 @@ def create(request): return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=["mysql", "mongo"]).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) @@ -111,13 +86,22 @@ def create(request): except Users.DoesNotExist: return JsonResponse({"status": 1, "msg": "负责人不存在", "data": []}) - # escape - db_name = MySQLdb.escape_string(db_name).decode("utf-8") - engine = get_engine(instance=instance) - exec_result = engine.execute( - db_name="information_schema", sql=f"create database {db_name};" - ) + if instance.db_type == 'mysql': + # escape + db_name = MySQLdb.escape_string(db_name).decode("utf-8") + exec_result = engine.execute(db_name="information_schema", sql=f"create database {db_name};") + elif instance.db_type == 'mongo': + exec_result = ResultSet() + try: + conn = engine.get_connection() + db = conn[db_name] + db.create_collection(name=f'archery-{db_name}') # mongo创建数据库,需要数据库存在数据才会显示数据库名称,这里创建一个archery-{db_name}的集合 + except Exception as e: + exec_result.error = f'创建数据库失败, 错误信息:{str(e)}' + + # 关闭连接 + engine.close() if exec_result.error: return JsonResponse({"status": 1, "msg": exec_result.error}) # 保存到数据库 @@ -133,6 +117,7 @@ def create(request): r = get_redis_connection("default") for key in r.scan_iter(match="*insRes*", count=2000): r.delete(key) + return JsonResponse({"status": 0, "msg": "", "data": []}) @@ -148,7 +133,7 @@ def edit(request): return JsonResponse({"status": 1, "msg": "参数不完整,请确认后提交", "data": []}) try: - instance = user_instances(request.user, db_type=["mysql"]).get(id=instance_id) + instance = user_instances(request.user, db_type=["mysql", "mongo"]).get(id=instance_id) except Instance.DoesNotExist: return JsonResponse({"status": 1, "msg": "你所在组未关联该实例", "data": []}) diff --git a/sql/models.py b/sql/models.py index 4cc7b316db..b66a3048f0 100755 --- a/sql/models.py +++ b/sql/models.py @@ -605,7 +605,8 @@ class InstanceAccount(models.Model): instance = models.ForeignKey(Instance, on_delete=models.CASCADE) user = fields.EncryptedCharField(verbose_name="账号", max_length=128) - host = models.CharField(verbose_name="主机", max_length=64) + host = models.CharField(verbose_name="主机", max_length=64) # mysql数据库存储主机信息 + db_name = models.CharField(verbose_name="数据库名称", max_length=128) # mongo数据库存储数据库名称 password = fields.EncryptedCharField( verbose_name="密码", max_length=128, default="", blank=True ) diff --git a/sql/templates/database.html b/sql/templates/database.html index 995a694ad6..e60bccbef6 100755 --- a/sql/templates/database.html +++ b/sql/templates/database.html @@ -7,6 +7,8 @@
@@ -141,8 +143,81 @@