利用Flask封装sqladvisor服务
使用sqladvisor需要预装很多系统组件,比如percona等,在不连外网的情况下,安装起来还是非常困难的。于是采用镜像的方式安装,这样就存在一个远程调用sqladvisor的问题,于是需要封装一个sqladvisor服务。此服务借鉴了一些archery开源项目的内容,特此说明。
from flask import Flask
from flask import request
import json
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
app = Flask(__name__)
import logging
import subprocess
import traceback
import pymysql
pymysql.install_as_MySQLdb()
logger = logging.getLogger('default')
class Plugin:
def __init__(self, path):
self.path = path
self.required_args = [] # must param
self.disable_args = [] # forbiden param
def check_args(self, args):
"""
check param
:return: {'status': 0, 'msg': 'ok', 'data': {}}
"""
args_check_result = {'status': 0, 'msg': 'ok', 'data': {}}
# check path
if self.path is None:
return {'status': 1, 'msg': 'exec path must not be null!', 'data': {}}
# check forbiden param
for arg in args.keys():
if arg in self.disable_args:
return {'status': 1, 'msg': '{arg}param forbiden'.format(arg=arg), 'data': {}}
# check must param
for req_arg in self.required_args:
if req_arg not in args.keys():
return {'status': 1, 'msg': 'need {arg} param'.format(arg=req_arg), 'data': {}}
elif args[req_arg] is None or args[req_arg] == '':
return {'status': 1, 'msg': '{arg} param cannot be null'.format(arg=req_arg), 'data': {}}
return args_check_result
def generate_args2cmd(self, args, shell):
"""
:return:
"""
@staticmethod
def execute_cmd(cmd_args, shell):
"""
:return:
"""
try:
p = subprocess.Popen(cmd_args,
shell=shell,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
#universal_newlines=True
)
return p
except Exception as e:
logger.error("exec failed \n{}".format(traceback.format_exc()))
raise RuntimeError('exec failed,reason:%s' % str(e))
class SQLAdvisor(Plugin):
def __init__(self):
self.path = "/usr/local/bin/sqladvisor"
self.required_args = ['q']
self.disable_args = []
super(Plugin, self).__init__()
def generate_args2cmd(self, args, shell):
"""
:param args:
:param shell:
:return:
"""
if shell:
cmd_args = self.path if self.path else ''
for name, value in args.items():
cmd_args += f' -{name} "{value}"'
else:
cmd_args = [self.path]
for name, value in args.items():
cmd_args.append(f'-{name}')
cmd_args.append(f'{value}')
return cmd_args
def user_instaces(ip,port):
dsnstr = "mysql://test:[email protected]:3307/archery?charset=utf8"
sql="SELECT USER,PASSWORD FROM ARCHERY.SQL_INSTANCE WHERE HOST='%s' AND PORT=%s"%(ip,port)
engine = create_engine(dsnstr)
DBsession = sessionmaker(bind=engine)
session = DBsession()
try:
ret = session.execute(sql).fetchone()
user=ret[0]
password=ret[1]
except Exception as e :
user="null"
password="null"
finally:
session.close()
return user,password
@app.route('/sqladvisor/', methods=['POST', 'GET'])
def sqladvisor():
req=request.json
host=req["ip"]
port=req["port"]
db_name=req["db_name"]
sql=req["sql"]
verbose=1
result = {'status': 0, 'msg': 'ok', 'data': []}
if sql is None or db_name is None:
result['status'] = 1
result['msg'] = 'sql or instance must not be null'
return json.dumps(result)
user, password=user_instaces(host,port)
if user =="null" or password=="null":
result['status'] = 1
result['msg'] = 'cannot find param in ARCHERY.SQL_INSTANCE'
return json.dumps(result)
sqladvisor_path = "/usr/local/bin/sqladvisor"
if sqladvisor_path is None:
result['status'] = 1
result['msg'] = 'please config SQLAdvisor path!'
return json.dumps(result)
sqladvisor = SQLAdvisor()
args = {"h": host,
"P": port,
"u": user,
"p": password,
"d": db_name,
"v": verbose,
"q": sql.strip().replace('"', '\\"').replace('`', '').replace('\n', ' ')
}
args_check_result = sqladvisor.check_args(args)
if args_check_result['status'] == 1:
return json.dumps(args_check_result)
cmd_args = sqladvisor.generate_args2cmd(args, shell=True)
try:
stdout, stderr = sqladvisor.execute_cmd(cmd_args, shell=True).communicate()
#print(stdout.decode('utf-8'),stderr.decode('utf-8'))
result['data'] = f"{stdout}{stderr}"
#print(result)
except RuntimeError as e:
result['status'] = 1
result['msg'] = str(e)
return json.dumps(result)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080)
#app.run(debug=True)