利用Flask封裝sqladvisor服務

利用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)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章